<!DOCTYPE html>
<html lang="en">





<head>
  <meta charset="UTF-8">
  <link rel="apple-touch-icon" sizes="76x76" href="/img/favicon.jpg">
  <link rel="icon" type="image/png" href="/img/favicon.jpg">
  <meta name="viewport"
        content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, shrink-to-fit=no">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  
  <meta name="theme-color" content="#2f4154">
  <meta name="description" content="">
  <meta name="author" content="John Doe">
  <meta name="keywords" content="">
  <title>JVM - Nyima&#39;s Blog</title>

  <link  rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/4.4.1/css/bootstrap.min.css" />


  <link  rel="stylesheet" href="https://cdn.staticfile.org/github-markdown-css/4.0.0/github-markdown.min.css" />
  <link  rel="stylesheet" href="/lib/hint/hint.min.css" />

  
    <link  rel="stylesheet" href="https://cdn.staticfile.org/highlight.js/10.0.0/styles/github-gist.min.css" />
  

  


<!-- 主题依赖的图标库，不要自行修改 -->

<link rel="stylesheet" href="//at.alicdn.com/t/font_1749284_yg9cfy8wd6.css">



<link rel="stylesheet" href="//at.alicdn.com/t/font_1736178_pjno9b9zyxs.css">


<link  rel="stylesheet" href="/css/main.css" />

<!-- 自定义样式保持在最底部 -->


  <script  src="/js/utils.js" ></script>
<meta name="generator" content="Hexo 4.2.1"></head>


<body>
  <header style="height: 70vh;">
    <nav id="navbar" class="navbar fixed-top  navbar-expand-lg navbar-dark scrolling-navbar">
  <div class="container">
    <a class="navbar-brand"
       href="/">&nbsp;<strong>Nyima</strong>&nbsp;</a>

    <button id="navbar-toggler-btn" class="navbar-toggler" type="button" data-toggle="collapse"
            data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <div class="animated-icon"><span></span><span></span><span></span></div>
    </button>

    <!-- Collapsible content -->
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav ml-auto text-center">
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/">
                <i class="iconfont icon-home-fill"></i>
                Home
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/archives/">
                <i class="iconfont icon-archive-fill"></i>
                Archives
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/categories/">
                <i class="iconfont icon-category-fill"></i>
                Categories
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/tags/">
                <i class="iconfont icon-tags-fill"></i>
                Tags
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/links/">
                <i class="iconfont icon-link-fill"></i>
                Links
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/about/">
                <i class="iconfont icon-user-fill"></i>
                About
              </a>
            </li>
          
        
        
          <li class="nav-item" id="search-btn">
            <a class="nav-link" data-toggle="modal" data-target="#modalSearch">&nbsp;&nbsp;<i
                class="iconfont icon-search"></i>&nbsp;&nbsp;</a>
          </li>
        
      </ul>
    </div>
  </div>
</nav>

    <div class="view intro-2" id="background" parallax=true
         style="background: url('/img/3.jpg') no-repeat center center;
           background-size: cover;">
      <div class="full-bg-img">
        <div class="mask flex-center" style="background-color: rgba(0, 0, 0, 0.3)">
          <div class="container text-center white-text fadeInUp">
            <span class="h2" id="subtitle">
              
            </span>

            
              
  <div class="mt-3 post-meta">
    <i class="iconfont icon-date-fill" aria-hidden="true"></i>
    <time datetime="2020-07-03 10:00">
      July 3, 2020 am
    </time>
  </div>


<div class="mt-1">
  
    
    <span class="post-meta mr-2">
      <i class="iconfont icon-chart"></i>
      18.8k 字
    </span>
  

  
    
    <span class="post-meta mr-2">
      <i class="iconfont icon-clock-fill"></i>
      
      
      240
       分钟
    </span>
  

  
  
</div>

            
          </div>

          
        </div>
      </div>
    </div>
  </header>

  <main>
    
      

<div class="container-fluid">
  <div class="row">
    <div class="d-none d-lg-block col-lg-2"></div>
    <div class="col-lg-8 nopadding-md">
      <div class="container nopadding-md" id="board-ctn">
        <div class="py-5" id="board">
          <div class="post-content mx-auto" id="post">
            
            <article class="markdown-body">
              <h1 id="JVM学习"><a href="#JVM学习" class="headerlink" title="JVM学习"></a>JVM学习</h1><p>本博客是根据<a href="https://www.bilibili.com/video/BV1yE411Z7AP" target="_blank" rel="noopener"><strong>解密JVM【黑马程序员出品】</strong></a>教学视频学习时，所做的笔记</p>
<h2 id="一、什么是JVM"><a href="#一、什么是JVM" class="headerlink" title="一、什么是JVM"></a>一、什么是JVM</h2><h3 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h3><p>Java Virtual Machine，JAVA程序的<strong>运行环境</strong>（JAVA二进制字节码的运行环境）</p>
<h3 id="好处"><a href="#好处" class="headerlink" title="好处"></a>好处</h3><ul>
<li>一次编写，到处运行</li>
<li>自动内存管理，垃圾回收机制</li>
<li>数组下标越界检查</li>
</ul>
<h3 id="比较"><a href="#比较" class="headerlink" title="比较"></a>比较</h3><p>JVM JRE JDK的区别</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150422.png" srcset="/img/loading.gif" alt=""></p>
<h2 id="二、内存结构"><a href="#二、内存结构" class="headerlink" title="二、内存结构"></a>二、内存结构</h2><h3 id="整体架构"><a href="#整体架构" class="headerlink" title="整体架构"></a><strong>整体架构</strong></h3><p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150440.png" srcset="/img/loading.gif" alt=""></p>
<h3 id="1、程序计数器"><a href="#1、程序计数器" class="headerlink" title="1、程序计数器"></a>1、程序计数器</h3><h4 id="作用"><a href="#作用" class="headerlink" title="作用"></a>作用</h4><p>用于保存JVM中下一条所要执行的指令的地址</p>
<h4 id="特点"><a href="#特点" class="headerlink" title="特点"></a>特点</h4><ul>
<li>线程私有<ul>
<li>CPU会为每个线程分配时间片，当当前线程的时间片使用完以后，CPU就会去执行另一个线程中的代码</li>
<li>程序计数器是<strong>每个线程</strong>所<strong>私有</strong>的，当另一个线程的时间片用完，又返回来执行当前线程的代码时，通过程序计数器可以知道应该执行哪一句指令</li>
</ul>
</li>
<li>不会存在内存溢出</li>
</ul>
<h3 id="2、虚拟机栈"><a href="#2、虚拟机栈" class="headerlink" title="2、虚拟机栈"></a>2、虚拟机栈</h3><h4 id="定义-1"><a href="#定义-1" class="headerlink" title="定义"></a>定义</h4><ul>
<li><p>每个<strong>线程</strong>运行需要的内存空间，称为<strong>虚拟机栈</strong></p>
</li>
<li><p>每个栈由多个<strong>栈帧</strong>组成，对应着每次调用方法时所占用的内存</p>
</li>
<li><p>每个线程只能有<strong>一个活动栈帧</strong>，对应着<strong>当前正在执行的方法</strong></p>
</li>
</ul>
<h4 id="演示"><a href="#演示" class="headerlink" title="演示"></a>演示</h4><p>代码</p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Main</span> </span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		method1();
	&#125;

	<span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">method1</span><span class="hljs-params">()</span> </span>&#123;
		method2(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>);
	&#125;

	<span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">method2</span><span class="hljs-params">(<span class="hljs-keyword">int</span> a, <span class="hljs-keyword">int</span> b)</span> </span>&#123;
		<span class="hljs-keyword">int</span> c = a + b;
		<span class="hljs-keyword">return</span> c;
	&#125;
&#125;</code></pre>

<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150534.png" srcset="/img/loading.gif" alt=""></p>
<p>在控制台中可以看到，主类中的方法在进入虚拟机栈的时候，符合栈的特点</p>
<h4 id="问题辨析"><a href="#问题辨析" class="headerlink" title="问题辨析"></a>问题辨析</h4><ul>
<li>垃圾回收是否涉及栈内存？<ul>
<li><strong>不需要</strong>。因为虚拟机栈中是由一个个栈帧组成的，在方法执行完毕后，对应的栈帧就会被弹出栈。所以无需通过垃圾回收机制去回收内存。</li>
</ul>
</li>
<li>栈内存的分配越大越好吗？<ul>
<li>不是。因为<strong>物理内存是一定的</strong>，栈内存越大，可以支持更多的递归调用，但是可执行的线程数就会越少。</li>
</ul>
</li>
<li>方法内的局部变量是否是线程安全的？<ul>
<li>如果方法内<strong>局部变量没有逃离方法的作用范围</strong>，则是<strong>线程安全</strong>的</li>
<li>如果如果<strong>局部变量引用了对象</strong>，并<strong>逃离了方法的作用范围</strong>，则需要考虑线程安全问题</li>
</ul>
</li>
</ul>
<h4 id="内存溢出"><a href="#内存溢出" class="headerlink" title="内存溢出"></a>内存溢出</h4><p><strong>Java.lang.stackOverflowError</strong> 栈内存溢出</p>
<p><strong>发生原因</strong></p>
<ul>
<li>虚拟机栈中，<strong>栈帧过多</strong>（无限递归）</li>
<li>每个栈帧<strong>所占用过大</strong></li>
</ul>
<h4 id="线程运行诊断"><a href="#线程运行诊断" class="headerlink" title="线程运行诊断"></a>线程运行诊断</h4><p>CPU占用过高</p>
<ul>
<li>Linux环境下运行某些程序的时候，可能导致CPU的占用过高，这时需要定位占用CPU过高的线程<ul>
<li><strong>top</strong>命令，查看是哪个<strong>进程</strong>占用CPU过高</li>
<li><strong>ps H -eo pid, tid（线程id）, %cpu | grep 刚才通过top查到的进程号</strong>    通过ps命令进一步查看是哪个线程占用CPU过高</li>
<li><strong>jstack 进程id</strong>  通过查看进程中的线程的nid，刚才通过ps命令看到的tid来<strong>对比定位</strong>，注意jstack查找出的线程id是<strong>16进制的</strong>，<strong>需要转换</strong></li>
</ul>
</li>
</ul>
<h3 id="3、本地方法栈"><a href="#3、本地方法栈" class="headerlink" title="3、本地方法栈"></a>3、本地方法栈</h3><p>一些带有<strong>native关键字</strong>的方法就是需要JAVA去调用本地的C或者C++方法，因为JAVA有时候没法直接和操作系统底层交互，所以需要用到本地方法</p>
<h3 id="4、堆"><a href="#4、堆" class="headerlink" title="4、堆"></a>4、堆</h3><h4 id="定义-2"><a href="#定义-2" class="headerlink" title="定义"></a>定义</h4><p>通过new关键字<strong>创建的对象</strong>都会被放在堆内存</p>
<h4 id="特点-1"><a href="#特点-1" class="headerlink" title="特点"></a>特点</h4><ul>
<li><strong>所有线程共享</strong>，堆内存中的对象都需要<strong>考虑线程安全问题</strong></li>
<li>有垃圾回收机制</li>
</ul>
<h4 id="堆内存溢出"><a href="#堆内存溢出" class="headerlink" title="堆内存溢出"></a>堆内存溢出</h4><p><strong>java.lang.OutofMemoryError</strong> ：java heap space. 堆内存溢出</p>
<h4 id="堆内存诊断"><a href="#堆内存诊断" class="headerlink" title="堆内存诊断"></a>堆内存诊断</h4><p><strong>jps</strong></p>
<p><strong>jmap</strong></p>
<p><strong>jconsole</strong></p>
<p><strong>jvirsalvm</strong></p>
<h3 id="5、方法区"><a href="#5、方法区" class="headerlink" title="5、方法区"></a>5、方法区</h3><h4 id="结构"><a href="#结构" class="headerlink" title="结构"></a>结构</h4><p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150547.png" srcset="/img/loading.gif" alt=""></p>
<h4 id="内存溢出-1"><a href="#内存溢出-1" class="headerlink" title="内存溢出"></a>内存溢出</h4><ul>
<li>1.8以前会导致<strong>永久代</strong>内存溢出</li>
<li>1.8以后会导致<strong>元空间</strong>内存溢出</li>
</ul>
<h4 id="常量池"><a href="#常量池" class="headerlink" title="常量池"></a>常量池</h4><p>二进制字节码的组成：类的基本信息、常量池、类的方法定义（包含了虚拟机指令）</p>
<p><strong>通过反编译来查看类的信息</strong></p>
<ul>
<li><p>获得对应类的.class文件</p>
<ul>
<li><p>在JDK对应的bin目录下运行cmd，<strong>也可以在IDEA控制台输入</strong></p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150602.png" srcset="/img/loading.gif" alt=""></p>
</li>
<li><p>输入 <strong>javac 对应类的绝对路径</strong></p>
<pre><code class="hljs taggerscript">F:<span class="hljs-symbol">\J</span>AVA<span class="hljs-symbol">\J</span>DK8.0<span class="hljs-symbol">\b</span>in&gt;javac F:<span class="hljs-symbol">\T</span>hread_study<span class="hljs-symbol">\s</span>rc<span class="hljs-symbol">\c</span>om<span class="hljs-symbol">\n</span>yima<span class="hljs-symbol">\J</span>VM<span class="hljs-symbol">\d</span>ay01<span class="hljs-symbol">\M</span>ain.java</code></pre>

<p>输入完成后，对应的目录下就会出现类的.class文件</p>
</li>
</ul>
</li>
<li><p>在控制台输入 javap -v 类的绝对路径</p>
<pre><code class="hljs taggerscript">javap -v F:<span class="hljs-symbol">\T</span>hread_study<span class="hljs-symbol">\s</span>rc<span class="hljs-symbol">\c</span>om<span class="hljs-symbol">\n</span>yima<span class="hljs-symbol">\J</span>VM<span class="hljs-symbol">\d</span>ay01<span class="hljs-symbol">\M</span>ain.class</code></pre>
</li>
<li><p>然后能在控制台看到反编译以后类的信息了</p>
<ul>
<li><p>类的基本信息</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150618.png" srcset="/img/loading.gif" alt=""></p>
</li>
<li><p>常量池</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150630.png" srcset="/img/loading.gif" alt=""></p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150641.png" srcset="/img/loading.gif" alt=""></p>
</li>
<li><p>虚拟机中执行编译的方法（框内的是真正编译执行的内容，<strong>#号的内容需要在常量池中查找</strong>）</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150653.png" srcset="/img/loading.gif" alt=""></p>
</li>
</ul>
</li>
</ul>
<h4 id="运行时常量池"><a href="#运行时常量池" class="headerlink" title="运行时常量池"></a>运行时常量池</h4><ul>
<li>常量池<ul>
<li>就是一张表（如上图中的constant pool），虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量信息</li>
</ul>
</li>
<li>运行时常量池<ul>
<li>常量池是<em>.class文件中的，当该*</em>类被加载以后<strong>，它的常量池信息就会</strong>放入运行时常量池<strong>，并把里面的</strong>符号地址变为真实地址**</li>
</ul>
</li>
</ul>
<h4 id="常量池与串池的关系"><a href="#常量池与串池的关系" class="headerlink" title="常量池与串池的关系"></a>常量池与串池的关系</h4><h5 id="串池StringTable"><a href="#串池StringTable" class="headerlink" title="串池StringTable"></a><strong>串池</strong>StringTable</h5><p><strong>特征</strong></p>
<ul>
<li>常量池中的字符串仅是符号，只有<strong>在被用到时才会转化为对象</strong></li>
<li>利用串池的机制，来避免重复创建字符串对象</li>
<li>字符串<strong>变量</strong>拼接的原理是<strong>StringBuilder</strong></li>
<li>字符串<strong>常量</strong>拼接的原理是<strong>编译器优化</strong></li>
<li>可以使用<strong>intern方法</strong>，主动将串池中还没有的字符串对象放入串池中</li>
<li><strong>注意</strong>：无论是串池还是堆里面的字符串，都是对象</li>
</ul>
<p>用来放字符串对象且里面的<strong>元素不重复</strong></p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">StringTableStudy</span> </span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		String a = <span class="hljs-string">"a"</span>; 
		String b = <span class="hljs-string">"b"</span>;
		String ab = <span class="hljs-string">"ab"</span>;
	&#125;
&#125;</code></pre>

<p>常量池中的信息，都会被加载到运行时常量池中，但这是a b ab 仅是常量池中的符号，<strong>还没有成为java字符串</strong></p>
<pre><code class="hljs angelscript"><span class="hljs-number">0</span>: ldc           #<span class="hljs-number">2</span>                  <span class="hljs-comment">// String a</span>
<span class="hljs-number">2</span>: astore_1
<span class="hljs-number">3</span>: ldc           #<span class="hljs-number">3</span>                  <span class="hljs-comment">// String b</span>
<span class="hljs-number">5</span>: astore_2
<span class="hljs-number">6</span>: ldc           #<span class="hljs-number">4</span>                  <span class="hljs-comment">// String ab</span>
<span class="hljs-number">8</span>: astore_3
<span class="hljs-number">9</span>: <span class="hljs-keyword">return</span></code></pre>

<p>当执行到 ldc #2 时，会把符号 a 变为 “a” 字符串对象，<strong>并放入串池中</strong>（hashtable结构 不可扩容）</p>
<p>当执行到 ldc #3 时，会把符号 b 变为 “b” 字符串对象，并放入串池中</p>
<p>当执行到 ldc #4 时，会把符号 ab 变为 “ab” 字符串对象，并放入串池中</p>
<p>最终<strong>StringTable [“a”, “b”, “ab”]</strong></p>
<p><strong>注意</strong>：字符串对象的创建都是<strong>懒惰的</strong>，只有当运行到那一行字符串且在串池中不存在的时候（如 ldc #2）时，该字符串才会被创建并放入串池中。</p>
<p>使用拼接<strong>字符串变量对象</strong>创建字符串的过程</p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">StringTableStudy</span> </span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		String a = <span class="hljs-string">"a"</span>;
		String b = <span class="hljs-string">"b"</span>;
		String ab = <span class="hljs-string">"ab"</span>;
		<span class="hljs-comment">//拼接字符串对象来创建新的字符串</span>
		String ab2 = a+b; 
	&#125;
&#125;</code></pre>

<p>反编译后的结果</p>
<pre><code class="hljs angelscript">	 Code:
      stack=<span class="hljs-number">2</span>, locals=<span class="hljs-number">5</span>, args_size=<span class="hljs-number">1</span>
         <span class="hljs-number">0</span>: ldc           #<span class="hljs-number">2</span>                  <span class="hljs-comment">// String a</span>
         <span class="hljs-number">2</span>: astore_1
         <span class="hljs-number">3</span>: ldc           #<span class="hljs-number">3</span>                  <span class="hljs-comment">// String b</span>
         <span class="hljs-number">5</span>: astore_2
         <span class="hljs-number">6</span>: ldc           #<span class="hljs-number">4</span>                  <span class="hljs-comment">// String ab</span>
         <span class="hljs-number">8</span>: astore_3
         <span class="hljs-number">9</span>: new           #<span class="hljs-number">5</span>                  <span class="hljs-comment">// class java/lang/StringBuilder</span>
        <span class="hljs-number">12</span>: dup
        <span class="hljs-number">13</span>: invokespecial #<span class="hljs-number">6</span>                  <span class="hljs-comment">// Method java/lang/StringBuilder."&lt;init&gt;":()V</span>
        <span class="hljs-number">16</span>: aload_1
        <span class="hljs-number">17</span>: invokevirtual #<span class="hljs-number">7</span>                  <span class="hljs-comment">// Method java/lang/StringBuilder.append:(Ljava/lang/String</span>
;)Ljava/lang/StringBuilder;
        <span class="hljs-number">20</span>: aload_2
        <span class="hljs-number">21</span>: invokevirtual #<span class="hljs-number">7</span>                  <span class="hljs-comment">// Method java/lang/StringBuilder.append:(Ljava/lang/String</span>
;)Ljava/lang/StringBuilder;
        <span class="hljs-number">24</span>: invokevirtual #<span class="hljs-number">8</span>                  <span class="hljs-comment">// Method java/lang/StringBuilder.toString:()Ljava/lang/Str</span>
ing;
        <span class="hljs-number">27</span>: astore        <span class="hljs-number">4</span>
        <span class="hljs-number">29</span>: <span class="hljs-keyword">return</span></code></pre>

<p>通过拼接的方式来创建字符串的<strong>过程</strong>是：StringBuilder().append(“a”).append(“b”).toString()</p>
<p>最后的toString方法的返回值是一个<strong>新的字符串</strong>，但字符串的<strong>值</strong>和拼接的字符串一致，但是两个不同的字符串，<strong>一个存在于串池之中，一个存在于堆内存之中</strong></p>
<pre><code class="hljs java">String ab = <span class="hljs-string">"ab"</span>;
String ab2 = a+b;
<span class="hljs-comment">//结果为false,因为ab是存在于串池之中，ab2是由StringBuffer的toString方法所返回的一个对象，存在于堆内存之中</span>
System.out.println(ab == ab2);</code></pre>



<p>使用<strong>拼接字符串常量对象</strong>的方法创建字符串</p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">StringTableStudy</span> </span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		String a = <span class="hljs-string">"a"</span>;
		String b = <span class="hljs-string">"b"</span>;
		String ab = <span class="hljs-string">"ab"</span>;
		String ab2 = a+b;
		<span class="hljs-comment">//使用拼接字符串的方法创建字符串</span>
		String ab3 = <span class="hljs-string">"a"</span> + <span class="hljs-string">"b"</span>;
	&#125;
&#125;</code></pre>

<p>反编译后的结果</p>
<pre><code class="hljs angelscript"> 	  Code:
      stack=<span class="hljs-number">2</span>, locals=<span class="hljs-number">6</span>, args_size=<span class="hljs-number">1</span>
         <span class="hljs-number">0</span>: ldc           #<span class="hljs-number">2</span>                  <span class="hljs-comment">// String a</span>
         <span class="hljs-number">2</span>: astore_1
         <span class="hljs-number">3</span>: ldc           #<span class="hljs-number">3</span>                  <span class="hljs-comment">// String b</span>
         <span class="hljs-number">5</span>: astore_2
         <span class="hljs-number">6</span>: ldc           #<span class="hljs-number">4</span>                  <span class="hljs-comment">// String ab</span>
         <span class="hljs-number">8</span>: astore_3
         <span class="hljs-number">9</span>: new           #<span class="hljs-number">5</span>                  <span class="hljs-comment">// class java/lang/StringBuilder</span>
        <span class="hljs-number">12</span>: dup
        <span class="hljs-number">13</span>: invokespecial #<span class="hljs-number">6</span>                  <span class="hljs-comment">// Method java/lang/StringBuilder."&lt;init&gt;":()V</span>
        <span class="hljs-number">16</span>: aload_1
        <span class="hljs-number">17</span>: invokevirtual #<span class="hljs-number">7</span>                  <span class="hljs-comment">// Method java/lang/StringBuilder.append:(Ljava/lang/String</span>
;)Ljava/lang/StringBuilder;
        <span class="hljs-number">20</span>: aload_2
        <span class="hljs-number">21</span>: invokevirtual #<span class="hljs-number">7</span>                  <span class="hljs-comment">// Method java/lang/StringBuilder.append:(Ljava/lang/String</span>
;)Ljava/lang/StringBuilder;
        <span class="hljs-number">24</span>: invokevirtual #<span class="hljs-number">8</span>                  <span class="hljs-comment">// Method java/lang/StringBuilder.toString:()Ljava/lang/Str</span>
ing;
        <span class="hljs-number">27</span>: astore        <span class="hljs-number">4</span>
        <span class="hljs-comment">//ab3初始化时直接从串池中获取字符串</span>
        <span class="hljs-number">29</span>: ldc           #<span class="hljs-number">4</span>                  <span class="hljs-comment">// String ab</span>
        <span class="hljs-number">31</span>: astore        <span class="hljs-number">5</span>
        <span class="hljs-number">33</span>: <span class="hljs-keyword">return</span></code></pre>

<ul>
<li>使用<strong>拼接字符串常量</strong>的方法来创建新的字符串时，因为<strong>内容是常量，javac在编译期会进行优化，结果已在编译期确定为ab</strong>，而创建ab的时候已经在串池中放入了“ab”，所以ab3直接从串池中获取值，所以进行的操作和 ab = “ab” 一致。</li>
<li>使用<strong>拼接字符串变量</strong>的方法来创建新的字符串时，因为内容是变量，只能<strong>在运行期确定它的值，所以需要使用StringBuilder来创建</strong></li>
</ul>
<h5 id="intern方法-1-8"><a href="#intern方法-1-8" class="headerlink" title="intern方法 1.8"></a>intern方法 1.8</h5><p>调用字符串对象的intern方法，会将该字符串对象尝试放入到串池中</p>
<ul>
<li>如果串池中没有该字符串对象，则放入成功</li>
<li>如果有该字符串对象，则放入失败</li>
</ul>
<p>无论放入是否成功，都会返回<strong>串池中</strong>的字符串对象</p>
<p><strong>注意</strong>：此时如果调用intern方法成功，堆内存与串池中的字符串对象是同一个对象；如果失败，则不是同一个对象</p>
<p><strong>例1</strong></p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Main</span> </span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		<span class="hljs-comment">//"a" "b" 被放入串池中，str则存在于堆内存之中</span>
		String str = <span class="hljs-keyword">new</span> String(<span class="hljs-string">"a"</span>) + <span class="hljs-keyword">new</span> String(<span class="hljs-string">"b"</span>);
		<span class="hljs-comment">//调用str的intern方法，这时串池中没有"ab"，则会将该字符串对象放入到串池中，此时堆内存与串池中的"ab"是同一个对象</span>
		String st2 = str.intern();
		<span class="hljs-comment">//给str3赋值，因为此时串池中已有"ab"，则直接将串池中的内容返回</span>
		String str3 = <span class="hljs-string">"ab"</span>;
		<span class="hljs-comment">//因为堆内存与串池中的"ab"是同一个对象，所以以下两条语句打印的都为true</span>
		System.out.println(str == st2);
		System.out.println(str == str3);
	&#125;
&#125;</code></pre>



<p><strong>例2</strong></p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Main</span> </span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
        <span class="hljs-comment">//此处创建字符串对象"ab"，因为串池中还没有"ab"，所以将其放入串池中</span>
		String str3 = <span class="hljs-string">"ab"</span>;
        <span class="hljs-comment">//"a" "b" 被放入串池中，str则存在于堆内存之中</span>
		String str = <span class="hljs-keyword">new</span> String(<span class="hljs-string">"a"</span>) + <span class="hljs-keyword">new</span> String(<span class="hljs-string">"b"</span>);
        <span class="hljs-comment">//此时因为在创建str3时，"ab"已存在与串池中，所以放入失败，但是会返回串池中的"ab"</span>
		String str2 = str.intern();
        <span class="hljs-comment">//false</span>
		System.out.println(str == str2);
        <span class="hljs-comment">//false</span>
		System.out.println(str == str3);
        <span class="hljs-comment">//true</span>
		System.out.println(str2 == str3);
	&#125;
&#125;</code></pre>



<h5 id="intern方法-1-6"><a href="#intern方法-1-6" class="headerlink" title="intern方法 1.6"></a>intern方法 1.6</h5><p>调用字符串对象的intern方法，会将该字符串对象尝试放入到串池中</p>
<ul>
<li>如果串池中没有该字符串对象，会将该字符串对象复制一份，再放入到串池中</li>
<li>如果有该字符串对象，则放入失败</li>
</ul>
<p>无论放入是否成功，都会返回<strong>串池中</strong>的字符串对象</p>
<p><strong>注意</strong>：此时无论调用intern方法成功与否，串池中的字符串对象和堆内存中的字符串对象<strong>都不是同一个对象</strong></p>
<h4 id="StringTable-垃圾回收"><a href="#StringTable-垃圾回收" class="headerlink" title="StringTable 垃圾回收"></a>StringTable 垃圾回收</h4><p>StringTable在内存紧张时，会发生垃圾回收</p>
<h4 id="StringTable调优"><a href="#StringTable调优" class="headerlink" title="StringTable调优"></a>StringTable调优</h4><ul>
<li><p>因为StringTable是由HashTable实现的，所以可以<strong>适当增加HashTable桶的个数</strong>，来减少字符串放入串池所需要的时间</p>
<pre><code class="hljs routeros">-XX:<span class="hljs-attribute">StringTableSize</span>=xxxx</code></pre>

<p>​    </p>
</li>
<li><p>考虑是否需要将字符串对象入池</p>
<p>可以通过<strong>intern方法减少重复入池</strong></p>
</li>
</ul>
<h3 id="6、直接内存"><a href="#6、直接内存" class="headerlink" title="6、直接内存"></a>6、直接内存</h3><ul>
<li>属于操作系统，常见于NIO操作时，<strong>用于数据缓冲区</strong></li>
<li>分配回收成本较高，但读写性能高</li>
<li>不受JVM内存回收管理</li>
</ul>
<h4 id="文件读写流程"><a href="#文件读写流程" class="headerlink" title="文件读写流程"></a>文件读写流程</h4><p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150715.png" srcset="/img/loading.gif" alt=""></p>
<p><strong>使用了DirectBuffer</strong> </p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150736.png" srcset="/img/loading.gif" alt=""></p>
<p>直接内存是操作系统和Java代码<strong>都可以访问的一块区域</strong>，无需将代码从系统内存复制到Java堆内存，从而提高了效率</p>
<h4 id="释放原理"><a href="#释放原理" class="headerlink" title="释放原理"></a>释放原理</h4><p>直接内存的回收不是通过JVM的垃圾回收来释放的，而是通过<strong>unsafe.freeMemory</strong>来手动释放</p>
<p>通过</p>
<pre><code class="hljs java"><span class="hljs-comment">//通过ByteBuffer申请1M的直接内存</span>
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_1M);</code></pre>

<p>申请直接内存，但JVM并不能回收直接内存中的内容，它是如何实现回收的呢？</p>
<p><strong>allocateDirect的实现</strong></p>
<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> ByteBuffer <span class="hljs-title">allocateDirect</span><span class="hljs-params">(<span class="hljs-keyword">int</span> capacity)</span> </span>&#123;
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> DirectByteBuffer(capacity);
&#125;</code></pre>

<p>DirectByteBuffer类</p>
<pre><code class="hljs java">DirectByteBuffer(<span class="hljs-keyword">int</span> cap) &#123;   <span class="hljs-comment">// package-private</span>
   
    <span class="hljs-keyword">super</span>(-<span class="hljs-number">1</span>, <span class="hljs-number">0</span>, cap, cap);
    <span class="hljs-keyword">boolean</span> pa = VM.isDirectMemoryPageAligned();
    <span class="hljs-keyword">int</span> ps = Bits.pageSize();
    <span class="hljs-keyword">long</span> size = Math.max(<span class="hljs-number">1L</span>, (<span class="hljs-keyword">long</span>)cap + (pa ? ps : <span class="hljs-number">0</span>));
    Bits.reserveMemory(size, cap);

    <span class="hljs-keyword">long</span> base = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">try</span> &#123;
        base = unsafe.allocateMemory(size); <span class="hljs-comment">//申请内存</span>
    &#125; <span class="hljs-keyword">catch</span> (OutOfMemoryError x) &#123;
        Bits.unreserveMemory(size, cap);
        <span class="hljs-keyword">throw</span> x;
    &#125;
    unsafe.setMemory(base, size, (<span class="hljs-keyword">byte</span>) <span class="hljs-number">0</span>);
    <span class="hljs-keyword">if</span> (pa &amp;&amp; (base % ps != <span class="hljs-number">0</span>)) &#123;
        <span class="hljs-comment">// Round up to page boundary</span>
        address = base + ps - (base &amp; (ps - <span class="hljs-number">1</span>));
    &#125; <span class="hljs-keyword">else</span> &#123;
        address = base;
    &#125;
    cleaner = Cleaner.create(<span class="hljs-keyword">this</span>, <span class="hljs-keyword">new</span> Deallocator(base, size, cap)); <span class="hljs-comment">//通过虚引用，来实现直接内存的释放，this为虚引用的实际对象</span>
    att = <span class="hljs-keyword">null</span>;
&#125;</code></pre>

<p>这里调用了一个Cleaner的create方法，且后台线程还会对虚引用的对象监测，如果虚引用的实际对象（这里是DirectByteBuffer）被回收以后，就会调用Cleaner的clean方法，来清除直接内存中占用的内存</p>
<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">clean</span><span class="hljs-params">()</span> </span>&#123;
       <span class="hljs-keyword">if</span> (remove(<span class="hljs-keyword">this</span>)) &#123;
           <span class="hljs-keyword">try</span> &#123;
               <span class="hljs-keyword">this</span>.thunk.run(); <span class="hljs-comment">//调用run方法</span>
           &#125; <span class="hljs-keyword">catch</span> (<span class="hljs-keyword">final</span> Throwable var2) &#123;
               AccessController.doPrivileged(<span class="hljs-keyword">new</span> PrivilegedAction&lt;Void&gt;() &#123;
                   <span class="hljs-function"><span class="hljs-keyword">public</span> Void <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>&#123;
                       <span class="hljs-keyword">if</span> (System.err != <span class="hljs-keyword">null</span>) &#123;
                           (<span class="hljs-keyword">new</span> Error(<span class="hljs-string">"Cleaner terminated abnormally"</span>, var2)).printStackTrace();
                       &#125;

                       System.exit(<span class="hljs-number">1</span>);
                       <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
                   &#125;
               &#125;);
           &#125;</code></pre>

<p>对应对象的run方法</p>
<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>&#123;
    <span class="hljs-keyword">if</span> (address == <span class="hljs-number">0</span>) &#123;
        <span class="hljs-comment">// Paranoia</span>
        <span class="hljs-keyword">return</span>;
    &#125;
    unsafe.freeMemory(address); <span class="hljs-comment">//释放直接内存中占用的内存</span>
    address = <span class="hljs-number">0</span>;
    Bits.unreserveMemory(size, capacity);
&#125;</code></pre>



<h5 id="直接内存的回收机制总结"><a href="#直接内存的回收机制总结" class="headerlink" title="直接内存的回收机制总结"></a>直接内存的回收机制总结</h5><ul>
<li>使用了Unsafe类来完成直接内存的分配回收，回收需要主动调用freeMemory方法</li>
<li>ByteBuffer的实现内部使用了Cleaner（虚引用）来检测ByteBuffer。一旦ByteBuffer被垃圾回收，那么会由ReferenceHandler来调用Cleaner的clean方法调用freeMemory来释放内存</li>
</ul>
<h2 id="三、垃圾回收"><a href="#三、垃圾回收" class="headerlink" title="三、垃圾回收"></a>三、垃圾回收</h2><h3 id="1、如何判断对象可以回收"><a href="#1、如何判断对象可以回收" class="headerlink" title="1、如何判断对象可以回收"></a>1、如何判断对象可以回收</h3><h4 id="引用计数法"><a href="#引用计数法" class="headerlink" title="引用计数法"></a>引用计数法</h4><p>弊端：循环引用时，两个对象的计数都为1，导致两个对象都无法被释放</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150750.png" srcset="/img/loading.gif" alt=""></p>
<h4 id="可达性分析算法"><a href="#可达性分析算法" class="headerlink" title="可达性分析算法"></a>可达性分析算法</h4><ul>
<li>JVM中的垃圾回收器通过<strong>可达性分析</strong>来探索所有存活的对象</li>
<li>扫描堆中的对象，看能否沿着GC Root对象为起点的引用链找到该对象，如果<strong>找不到，则表示可以回收</strong></li>
<li>可以作为GC Root的对象<ul>
<li>虚拟机栈（栈帧中的本地变量表）中引用的对象。　</li>
<li>方法区中类静态属性引用的对象</li>
<li>方法区中常量引用的对象</li>
<li>本地方法栈中JNI（即一般说的Native方法）引用的对象</li>
</ul>
</li>
</ul>
<h4 id="五种引用"><a href="#五种引用" class="headerlink" title="五种引用"></a>五种引用</h4><p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150800.png" srcset="/img/loading.gif" alt=""></p>
<h5 id="强引用"><a href="#强引用" class="headerlink" title="强引用"></a>强引用</h5><p>只有GC Root<strong>都不引用</strong>该对象时，才会回收<strong>强引用</strong>对象</p>
<ul>
<li>如上图B、C对象都不引用A1对象时，A1对象才会被回收</li>
</ul>
<h5 id="软引用"><a href="#软引用" class="headerlink" title="软引用"></a>软引用</h5><p>当GC Root指向软引用对象时，在<strong>内存不足时</strong>，会<strong>回收软引用所引用的对象</strong></p>
<ul>
<li>如上图如果B对象不再引用A2对象且内存不足时，软引用所引用的A2对象就会被回收</li>
</ul>
<h6 id="软引用的使用"><a href="#软引用的使用" class="headerlink" title="软引用的使用"></a>软引用的使用</h6><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo1</span> </span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		<span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> _4M = <span class="hljs-number">4</span>*<span class="hljs-number">1024</span>*<span class="hljs-number">1024</span>;
		<span class="hljs-comment">//使用软引用对象 list和SoftReference是强引用，而SoftReference和byte数组则是软引用</span>
		List&lt;SoftReference&lt;<span class="hljs-keyword">byte</span>[]&gt;&gt; list = <span class="hljs-keyword">new</span> ArrayList&lt;&gt;();
		SoftReference&lt;<span class="hljs-keyword">byte</span>[]&gt; ref= <span class="hljs-keyword">new</span> SoftReference&lt;&gt;(<span class="hljs-keyword">new</span> <span class="hljs-keyword">byte</span>[_4M]);
	&#125;
&#125;</code></pre>

<p>如果在垃圾回收时发现内存不足，在回收软引用所指向的对象时，<strong>软引用本身不会被清理</strong></p>
<p>如果想要<strong>清理软引用</strong>，需要使<strong>用引用队列</strong></p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo1</span> </span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		<span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> _4M = <span class="hljs-number">4</span>*<span class="hljs-number">1024</span>*<span class="hljs-number">1024</span>;
		<span class="hljs-comment">//使用引用队列，用于移除引用为空的软引用对象</span>
		ReferenceQueue&lt;<span class="hljs-keyword">byte</span>[]&gt; queue = <span class="hljs-keyword">new</span> ReferenceQueue&lt;&gt;();
		<span class="hljs-comment">//使用软引用对象 list和SoftReference是强引用，而SoftReference和byte数组则是软引用</span>
		List&lt;SoftReference&lt;<span class="hljs-keyword">byte</span>[]&gt;&gt; list = <span class="hljs-keyword">new</span> ArrayList&lt;&gt;();
		SoftReference&lt;<span class="hljs-keyword">byte</span>[]&gt; ref= <span class="hljs-keyword">new</span> SoftReference&lt;&gt;(<span class="hljs-keyword">new</span> <span class="hljs-keyword">byte</span>[_4M]);

		<span class="hljs-comment">//遍历引用队列，如果有元素，则移除</span>
		Reference&lt;? extends <span class="hljs-keyword">byte</span>[]&gt; poll = queue.poll();
		<span class="hljs-keyword">while</span>(poll != <span class="hljs-keyword">null</span>) &#123;
			<span class="hljs-comment">//引用队列不为空，则从集合中移除该元素</span>
			list.remove(poll);
			<span class="hljs-comment">//移动到引用队列中的下一个元素</span>
			poll = queue.poll();
		&#125;
	&#125;
&#125;</code></pre>

<p><strong>大概思路为：</strong>查看引用队列中有无软引用，如果有，则将该软引用从存放它的集合中移除（这里为一个list集合）</p>
<h5 id="弱引用"><a href="#弱引用" class="headerlink" title="弱引用"></a>弱引用</h5><p>只有弱引用引用该对象时，在垃圾回收时，<strong>无论内存是否充足</strong>，都会回收弱引用所引用的对象</p>
<ul>
<li>如上图如果B对象不再引用A3对象，则A3对象会被回收</li>
</ul>
<p><strong>弱引用的使用和软引用类似</strong>，只是将 <strong>SoftReference 换为了 WeakReference</strong></p>
<h5 id="虚引用"><a href="#虚引用" class="headerlink" title="虚引用"></a><strong>虚引用</strong></h5><p>当虚引用对象所引用的对象被回收以后，虚引用对象就会被放入引用队列中，调用虚引用的方法</p>
<ul>
<li>虚引用的一个体现是<strong>释放直接内存所分配的内存</strong>，当引用的对象ByteBuffer被垃圾回收以后，虚引用对象Cleaner就会被放入引用队列中，然后调用Cleaner的clean方法来释放直接内存</li>
<li>如上图，B对象不再引用ByteBuffer对象，ByteBuffer就会被回收。但是直接内存中的内存还未被回收。这时需要将虚引用对象Cleaner放入引用队列中，然后调用它的clean方法来释放直接内存</li>
</ul>
<h5 id="终结器引用"><a href="#终结器引用" class="headerlink" title="终结器引用"></a>终结器引用</h5><p>所有的类都继承自Object类，Object类有一个finalize方法。当某个对象不再被其他的对象所引用时，会先将终结器引用对象放入引用队列中，然后根据终结器引用对象找到它所引用的对象，然后调用该对象的finalize方法。调用以后，该对象就可以被垃圾回收了</p>
<ul>
<li>如上图，B对象不再引用A4对象。这是终结器对象就会被放入引用队列中，引用队列会根据它，找到它所引用的对象。然后调用被引用对象的finalize方法。调用以后，该对象就可以被垃圾回收了</li>
</ul>
<h5 id="引用队列"><a href="#引用队列" class="headerlink" title="引用队列"></a>引用队列</h5><ul>
<li><p>软引用和弱引用<strong>可以配合</strong>引用队列</p>
<ul>
<li>在<strong>弱引用</strong>和<strong>虚引用</strong>所引用的对象被回收以后，会将这些引用放入引用队列中，方便一起回收这些软/弱引用对象</li>
</ul>
</li>
<li><p>虚引用和终结器引用<strong>必须配合</strong>引用队列</p>
<ul>
<li>虚引用和终结器引用在使用时会关联一个引用队列</li>
</ul>
</li>
</ul>
<h3 id="2、垃圾回收算法"><a href="#2、垃圾回收算法" class="headerlink" title="2、垃圾回收算法"></a>2、垃圾回收算法</h3><h4 id="标记-清除"><a href="#标记-清除" class="headerlink" title="标记-清除"></a>标记-清除</h4><p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150813.png" srcset="/img/loading.gif" alt=""></p>
<p><strong>定义</strong>：标记清除算法顾名思义，是指在虚拟机执行垃圾回收的过程中，先采用标记算法确定可回收对象，然后垃圾收集器根据标识清除相应的内容，给堆内存腾出相应的空间</p>
<ul>
<li>这里的腾出内存空间并不是将内存空间的字节清0，而是记录下这段内存的起始结束地址，下次分配内存的时候，会直接<strong>覆盖</strong>这段内存</li>
</ul>
<p><strong>缺点</strong>：<strong>容易产生大量的内存碎片</strong>，可能无法满足大对象的内存分配，一旦导致无法分配对象，那就会导致jvm启动gc，一旦启动gc，我们的应用程序就会暂停，这就导致应用的响应速度变慢</p>
<h4 id="标记-整理"><a href="#标记-整理" class="headerlink" title="标记-整理"></a>标记-整理</h4><p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150827.png" srcset="/img/loading.gif" alt=""></p>
<p>标记-整理 会将不被GC Root引用的对象回收，清楚其占用的内存空间。然后整理剩余的对象，可以有效避免因内存碎片而导致的问题，但是因为整体需要消耗一定的时间，所以效率较低</p>
<h4 id="复制"><a href="#复制" class="headerlink" title="复制"></a>复制</h4><p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150842.png" srcset="/img/loading.gif" alt=""></p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150856.png" srcset="/img/loading.gif" alt=""></p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150907.png" srcset="/img/loading.gif" alt=""></p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150919.png" srcset="/img/loading.gif" alt=""></p>
<p>将内存分为等大小的两个区域，FROM和TO（TO中为空）。先将被GC Root引用的对象从FROM放入TO中，再回收不被GC Root引用的对象。然后交换FROM和TO。这样也可以避免内存碎片的问题，但是会占用双倍的内存空间。</p>
<h3 id="3、分代回收"><a href="#3、分代回收" class="headerlink" title="3、分代回收"></a>3、分代回收</h3><p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150931.png" srcset="/img/loading.gif" alt=""></p>
<h4 id="回收流程"><a href="#回收流程" class="headerlink" title="回收流程"></a>回收流程</h4><p>新创建的对象都被放在了<strong>新生代的伊甸园</strong>中</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150939.png" srcset="/img/loading.gif" alt=""></p>
<p>当伊甸园中的内存不足时，就会进行一次垃圾回收，这时的回收叫做 <strong>Minor GC</strong></p>
<p>Minor GC 会将<strong>伊甸园和幸存区FROM</strong>存活的对象<strong>先</strong>复制到 <strong>幸存区 TO</strong>中， 并让其<strong>寿命加1</strong>，再<strong>交换两个幸存区</strong></p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150946.png" srcset="/img/loading.gif" alt=""></p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150955.png" srcset="/img/loading.gif" alt=""></p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151002.png" srcset="/img/loading.gif" alt=""></p>
<p>再次创建对象，若新生代的伊甸园又满了，则会<strong>再次触发 Minor GC</strong>（会触发 <strong>stop the world</strong>， 暂停其他用户线程，只让垃圾回收线程工作），这时不仅会回收伊甸园中的垃圾，<strong>还会回收幸存区中的垃圾</strong>，再将活跃对象复制到幸存区TO中。回收以后会交换两个幸存区，并让幸存区中的对象<strong>寿命加1</strong></p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151010.png" srcset="/img/loading.gif" alt=""></p>
<p>如果幸存区中的对象的<strong>寿命超过某个阈值</strong>（最大为15，4bit），就会被<strong>放入老年代</strong>中</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151018.png" srcset="/img/loading.gif" alt=""></p>
<p>如果新生代老年代中的内存都满了，就会先触发Minor GC，再触发<strong>Full GC</strong>，扫描<strong>新生代和老年代中</strong>所有不再使用的对象并回收</p>
<h4 id="GC-分析"><a href="#GC-分析" class="headerlink" title="GC 分析"></a>GC 分析</h4><h5 id="大对象处理策略"><a href="#大对象处理策略" class="headerlink" title="大对象处理策略"></a>大对象处理策略</h5><p>当遇到一个<strong>较大的对象</strong>时，就算新生代的<strong>伊甸园</strong>为空，也<strong>无法容纳该对象</strong>时，会将该对象<strong>直接晋升为老年代</strong></p>
<h5 id="线程内存溢出"><a href="#线程内存溢出" class="headerlink" title="线程内存溢出"></a>线程内存溢出</h5><p>某个线程的内存溢出了而抛异常（out of memory），不会让其他的线程结束运行</p>
<p>这是因为当一个线程<strong>抛出OOM异常后</strong>，<strong>它所占据的内存资源会全部被释放掉</strong>，从而不会影响其他线程的运行，<strong>进程依然正常</strong></p>
<h3 id="4、垃圾回收器"><a href="#4、垃圾回收器" class="headerlink" title="4、垃圾回收器"></a>4、垃圾回收器</h3><h4 id="相关概念"><a href="#相关概念" class="headerlink" title="相关概念"></a>相关概念</h4><p><strong>并行收集</strong>：指多条垃圾收集线程并行工作，但此时<strong>用户线程仍处于等待状态</strong>。</p>
<p><strong>并发收集</strong>：指用户线程与垃圾收集线程<strong>同时工作</strong>（不一定是并行的可能会交替执行）。<strong>用户程序在继续运行</strong>，而垃圾收集程序运行在另一个CPU上</p>
<p><strong>吞吐量</strong>：即CPU用于<strong>运行用户代码的时间</strong>与CPU<strong>总消耗时间</strong>的比值（吞吐量 = 运行用户代码时间 / ( 运行用户代码时间 + 垃圾收集时间 )），也就是。例如：虚拟机共运行100分钟，垃圾收集器花掉1分钟，那么吞吐量就是99%</p>
<h4 id="串行"><a href="#串行" class="headerlink" title="串行"></a>串行</h4><ul>
<li>单线程</li>
<li>内存较小，个人电脑（CPU核数较少）</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151027.png" srcset="/img/loading.gif" alt=""></p>
<p><strong>安全点</strong>：让其他线程都在这个点停下来，以免垃圾回收时移动对象地址，使得其他线程找不到被移动的对象</p>
<p>因为是串行的，所以只有一个垃圾回收线程。且在该线程执行回收工作时，其他线程进入<strong>阻塞</strong>状态</p>
<h5 id="Serial-收集器"><a href="#Serial-收集器" class="headerlink" title="Serial 收集器"></a>Serial 收集器</h5><p>Serial收集器是最基本的、发展历史最悠久的收集器</p>
<p><strong>特点：</strong>单线程、简单高效（与其他收集器的单线程相比），采用<strong>复制算法</strong>。对于限定单个CPU的环境来说，Serial收集器由于没有线程交互的开销，专心做垃圾收集自然可以获得最高的单线程手机效率。收集器进行垃圾回收时，必须暂停其他所有的工作线程，直到它结束（Stop The World）</p>
<h5 id="ParNew-收集器"><a href="#ParNew-收集器" class="headerlink" title="ParNew 收集器"></a>ParNew 收集器</h5><p>ParNew收集器其实就是Serial收集器的多线程版本</p>
<p><strong>特点</strong>：多线程、ParNew收集器默认开启的收集线程数与CPU的数量相同，在CPU非常多的环境中，可以使用-XX:ParallelGCThreads参数来限制垃圾收集的线程数。和Serial收集器一样存在Stop The World问题</p>
<h5 id="Serial-Old-收集器"><a href="#Serial-Old-收集器" class="headerlink" title="Serial Old 收集器"></a>Serial Old 收集器</h5><p>Serial Old是Serial收集器的老年代版本</p>
<p><strong>特点</strong>：同样是单线程收集器，采用<strong>标记-整理算法</strong></p>
<h4 id="吞吐量优先"><a href="#吞吐量优先" class="headerlink" title="吞吐量优先"></a>吞吐量优先</h4><ul>
<li>多线程</li>
<li>堆内存较大，多核CPU</li>
<li>单位时间内，STW（stop the world，停掉其他所有工作线程）时间最短</li>
<li><strong>JDK1.8默认使用</strong>的垃圾回收器</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151039.png" srcset="/img/loading.gif" alt=""></p>
<h5 id="Parallel-Scavenge-收集器"><a href="#Parallel-Scavenge-收集器" class="headerlink" title="Parallel Scavenge 收集器"></a>Parallel Scavenge 收集器</h5><p>与吞吐量关系密切，故也称为吞吐量优先收集器</p>
<p><strong>特点</strong>：属于新生代收集器也是采用<strong>复制算法</strong>的收集器（用到了新生代的幸存区），又是并行的多线程收集器（与ParNew收集器类似）</p>
<p>该收集器的目标是达到一个可控制的吞吐量。还有一个值得关注的点是：<strong>GC自适应调节策略</strong>（与ParNew收集器最重要的一个区别）</p>
<p><strong>GC自适应调节策略</strong>：Parallel  Scavenge收集器可设置-XX:+UseAdptiveSizePolicy参数。当开关打开时<strong>不需要</strong>手动指定新生代的大小（-Xmn）、Eden与Survivor区的比例（-XX:SurvivorRation）、晋升老年代的对象年龄（-XX:PretenureSizeThreshold）等，虚拟机会根据系统的运行状况收集性能监控信息，动态设置这些参数以提供最优的停顿时间和最高的吞吐量，这种调节方式称为GC的自适应调节策略。</p>
<p>Parallel Scavenge收集器使用两个参数控制吞吐量：</p>
<ul>
<li>XX:MaxGCPauseMillis 控制最大的垃圾收集停顿时间</li>
<li>XX:GCRatio 直接设置吞吐量的大小</li>
</ul>
<h5 id="Parallel-Old-收集器"><a href="#Parallel-Old-收集器" class="headerlink" title="Parallel Old 收集器"></a><strong>Parallel Old 收集器</strong></h5><p>是Parallel Scavenge收集器的老年代版本</p>
<p><strong>特点</strong>：多线程，采用<strong>标记-整理算法</strong>（老年代没有幸存区）</p>
<h4 id="响应时间优先"><a href="#响应时间优先" class="headerlink" title="响应时间优先"></a>响应时间优先</h4><ul>
<li><p>多线程</p>
</li>
<li><p>堆内存较大，多核CPU</p>
</li>
<li><p>尽可能让单次STW时间变短（尽量不影响其他线程运行）</p>
</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151052.png" srcset="/img/loading.gif" alt=""></p>
<h5 id="CMS-收集器"><a href="#CMS-收集器" class="headerlink" title="CMS 收集器"></a>CMS 收集器</h5><p>Concurrent Mark Sweep，一种以获取<strong>最短回收停顿时间</strong>为目标的<strong>老年代</strong>收集器</p>
<p><strong>特点</strong>：基于<strong>标记-清除算法</strong>实现。并发收集、低停顿，但是会产生内存碎片</p>
<p><strong>应用场景</strong>：适用于注重服务的响应速度，希望系统停顿时间最短，给用户带来更好的体验等场景下。如web程序、b/s服务</p>
<p><strong>CMS收集器的运行过程分为下列4步：</strong></p>
<p><strong>初始标记</strong>：标记GC Roots能直接到的对象。速度很快但是<strong>仍存在Stop The World问题</strong></p>
<p><strong>并发标记</strong>：进行GC Roots Tracing 的过程，找出存活对象且用户线程可并发执行</p>
<p><strong>重新标记</strong>：为了<strong>修正并发标记期间</strong>因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录。仍然存在Stop The World问题</p>
<p><strong>并发清除</strong>：对标记的对象进行清除回收</p>
<p> CMS收集器的内存回收过程是与用户线程一起<strong>并发执行</strong>的</p>
<h4 id="G1"><a href="#G1" class="headerlink" title="G1"></a>G1</h4><h5 id="定义："><a href="#定义：" class="headerlink" title="定义："></a><strong>定义</strong>：</h5><p>Garbage First</p>
<p>JDK 9以后默认使用，而且替代了CMS 收集器</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200909201212.png" srcset="/img/loading.gif" alt=""></p>
<h5 id="适用场景"><a href="#适用场景" class="headerlink" title="适用场景"></a>适用场景</h5><ul>
<li>同时注重吞吐量和低延迟（响应时间）</li>
<li>超大堆内存（内存大的），会将堆内存划分为多个<strong>大小相等</strong>的区域</li>
<li>整体上是<strong>标记-整理</strong>算法，两个区域之间是<strong>复制</strong>算法</li>
</ul>
<p><strong>相关参数</strong>：JDK8 并不是默认开启的，所需要参数开启</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151100.png" srcset="/img/loading.gif" alt=""></p>
<h5 id="G1垃圾回收阶段"><a href="#G1垃圾回收阶段" class="headerlink" title="G1垃圾回收阶段"></a>G1垃圾回收阶段</h5><p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151109.png" srcset="/img/loading.gif" alt=""></p>
<p>新生代伊甸园垃圾回收—–&gt;内存不足，新生代回收+并发标记—–&gt;回收新生代伊甸园、幸存区、老年代内存——&gt;新生代伊甸园垃圾回收(重新开始)</p>
<h5 id="Young-Collection"><a href="#Young-Collection" class="headerlink" title="Young Collection"></a>Young Collection</h5><p><strong>分区算法region</strong></p>
<p>分代是按对象的生命周期划分，分区则是将堆空间划分连续几个不同小区间，每一个小区间独立回收，可以控制一次回收多少个小区间，方便控制 GC 产生的停顿时间</p>
<p>E：伊甸园    S：幸存区    O：老年代</p>
<ul>
<li>会STW</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151119.png" srcset="/img/loading.gif" alt=""></p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151129.png" srcset="/img/loading.gif" alt=""></p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151140.png" srcset="/img/loading.gif" alt=""></p>
<h5 id="Young-Collection-CM"><a href="#Young-Collection-CM" class="headerlink" title="Young Collection + CM"></a>Young Collection + CM</h5><p>CM：并发标记</p>
<ul>
<li>在 Young GC 时会<strong>对 GC Root 进行初始标记</strong></li>
<li>在老年代<strong>占用堆内存的比例</strong>达到阈值时，对进行并发标记（不会STW），阈值可以根据用户来进行设定</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151150.png" srcset="/img/loading.gif" alt=""></p>
<h5 id="Mixed-Collection"><a href="#Mixed-Collection" class="headerlink" title="Mixed Collection"></a>Mixed Collection</h5><p> 会对E S O 进行<strong>全面的回收</strong></p>
<ul>
<li>最终标记</li>
<li><strong>拷贝</strong>存活</li>
</ul>
<p>-XX:MaxGCPauseMills:xxx    用于指定最长的停顿时间</p>
<p><strong>问</strong>：为什么有的老年代被拷贝了，有的没拷贝？</p>
<p>因为指定了最大停顿时间，如果对所有老年代都进行回收，耗时可能过高。为了保证时间不超过设定的停顿时间，会<strong>回收最有价值的老年代</strong>（回收后，能够得到更多内存）</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151201.png" srcset="/img/loading.gif" alt=""></p>
<h5 id="Full-GC"><a href="#Full-GC" class="headerlink" title="Full GC"></a>Full GC</h5><p>G1在老年代内存不足时（老年代所占内存超过阈值）</p>
<ul>
<li>如果垃圾产生速度慢于垃圾回收速度，不会触发Full GC，还是并发地进行清理</li>
<li>如果垃圾产生速度快于垃圾回收速度，便会触发Full GC</li>
</ul>
<h5 id="Young-Collection-跨代引用"><a href="#Young-Collection-跨代引用" class="headerlink" title="Young Collection 跨代引用"></a>Young Collection 跨代引用</h5><ul>
<li>新生代回收的跨代引用（老年代引用新生代）问题</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151211.png" srcset="/img/loading.gif" alt=""></p>
<ul>
<li><p>卡表与Remembered Set</p>
<ul>
<li>Remembered Set 存在于E中，用于保存新生代对象对应的脏卡<ul>
<li>脏卡：O被划分为多个区域（一个区域512K），如果该区域引用了新生代对象，则该区域被称为脏卡</li>
</ul>
</li>
</ul>
</li>
<li><p>在引用变更时通过post-write barried + dirty card queue</p>
</li>
<li><p>concurrent refinement threads  更新 Remembered Set</p>
</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151222.png" srcset="/img/loading.gif" alt=""></p>
<h5 id="Remark"><a href="#Remark" class="headerlink" title="Remark"></a>Remark</h5><p>重新标记阶段</p>
<p>在垃圾回收时，收集器处理对象的过程中</p>
<p>黑色：已被处理，需要保留的  灰色：正在处理中的 白色：还未处理的</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151229.png" srcset="/img/loading.gif" alt=""></p>
<p>但是在<strong>并发标记过程中</strong>，有可能A被处理了以后未引用C，但该处理过程还未结束，在处理过程结束之前A引用了C，这时就会用到remark</p>
<p>过程如下</p>
<ul>
<li>之前C未被引用，这时A引用了C，就会给C加一个写屏障，写屏障的指令会被执行，将C放入一个队列当中，并将C变为 处理中 状态</li>
<li>在<strong>并发标记</strong>阶段结束以后，重新标记阶段会STW，然后将放在该队列中的对象重新处理，发现有强引用引用它，就会处理它</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151239.png" srcset="/img/loading.gif" alt=""></p>
<h5 id="JDK-8u20-字符串去重"><a href="#JDK-8u20-字符串去重" class="headerlink" title="JDK 8u20 字符串去重"></a>JDK 8u20 字符串去重</h5><p>过程</p>
<ul>
<li>将所有新分配的字符串（底层是char[]）放入一个队列</li>
<li>当新生代回收时，G1并发检查是否有重复的字符串</li>
<li>如果字符串的值一样，就让他们<strong>引用同一个字符串对象</strong></li>
<li>注意，其与String.intern的区别<ul>
<li>intern关注的是字符串对象</li>
<li>字符串去重关注的是char[]</li>
<li>在JVM内部，使用了不同的字符串标</li>
</ul>
</li>
</ul>
<p>优点与缺点</p>
<ul>
<li>节省了大量内存</li>
<li>新生代回收时间略微增加，导致略微多占用CPU</li>
</ul>
<h5 id="JDK-8u40-并发标记类卸载"><a href="#JDK-8u40-并发标记类卸载" class="headerlink" title="JDK 8u40 并发标记类卸载"></a>JDK 8u40 并发标记类卸载</h5><p>在并发标记阶段结束以后，就能知道哪些类不再被使用。如果一个类加载器的所有类都不在使用，则卸载它所加载的所有类</p>
<h5 id="JDK-8u60-回收巨型对象"><a href="#JDK-8u60-回收巨型对象" class="headerlink" title="JDK 8u60 回收巨型对象"></a>JDK 8u60 回收巨型对象</h5><ul>
<li>一个对象大于region的一半时，就称为巨型对象</li>
<li>G1不会对巨型对象进行拷贝</li>
<li>回收时被优先考虑</li>
<li>G1会跟踪老年代所有incoming引用，如果老年代incoming引用为0的巨型对象就可以在新生代垃圾回收时处理掉</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151249.png" srcset="/img/loading.gif" alt=""></p>
<h3 id="5、GC-调优"><a href="#5、GC-调优" class="headerlink" title="5、GC 调优"></a>5、GC 调优</h3><p>查看虚拟机参数命令</p>
<pre><code class="hljs taggerscript">"F:<span class="hljs-symbol">\J</span>AVA<span class="hljs-symbol">\J</span>DK8.0<span class="hljs-symbol">\b</span>in<span class="hljs-symbol">\j</span>ava" -XX:+PrintFlagsFinal -version | findstr "GC"</code></pre>

<p>可以根据参数去查询具体的信息</p>
<h4 id="调优领域"><a href="#调优领域" class="headerlink" title="调优领域"></a>调优领域</h4><ul>
<li><p>内存</p>
</li>
<li><p>锁竞争</p>
</li>
<li><p>CPU占用</p>
</li>
<li><p>IO</p>
</li>
<li><p>GC</p>
</li>
</ul>
<h4 id="确定目标"><a href="#确定目标" class="headerlink" title="确定目标"></a>确定目标</h4><p>低延迟/高吞吐量？ 选择合适的GC</p>
<ul>
<li><p>CMS G1 ZGC</p>
</li>
<li><p>ParallelGC</p>
</li>
<li><p>Zing</p>
</li>
</ul>
<h4 id="最快的GC是不发生GC"><a href="#最快的GC是不发生GC" class="headerlink" title="最快的GC是不发生GC"></a>最快的GC是不发生GC</h4><p>首先排除减少因为自身编写的代码而引发的内存问题 </p>
<ul>
<li>查看Full GC前后的内存占用，考虑以下几个问题<ul>
<li>数据是不是太多？</li>
<li>数据表示是否太臃肿<ul>
<li>对象图</li>
<li>对象大小</li>
</ul>
</li>
<li>是否存在内存泄漏</li>
</ul>
</li>
</ul>
<h4 id="新生代调优"><a href="#新生代调优" class="headerlink" title="新生代调优"></a>新生代调优</h4><ul>
<li><p>新生代的特点</p>
<ul>
<li>所有的new操作分配内存都是非常廉价的<ul>
<li>TLAB</li>
</ul>
</li>
<li>死亡对象回收零代价</li>
<li>大部分对象用过即死（朝生夕死）</li>
<li>MInor GC 所用时间远小于Full GC</li>
</ul>
</li>
<li><p>新生代内存越大越好么？</p>
<ul>
<li><p>不是</p>
<ul>
<li>新生代内存太小：频繁触发Minor GC，会STW，会使得吞吐量下降</li>
<li>新生代内存太大：老年代内存占比有所降低，会更频繁地触发Full GC。而且触发Minor GC时，清理新生代所花费的时间会更长</li>
</ul>
</li>
<li><p>新生代内存设置为内容纳[并发量*(请求-响应)]的数据为宜</p>
</li>
</ul>
</li>
</ul>
<h4 id="幸存区调优"><a href="#幸存区调优" class="headerlink" title="幸存区调优"></a>幸存区调优</h4><ul>
<li>幸存区需要能够保存 <strong>当前活跃对象</strong>+<strong>需要晋升的对象</strong></li>
<li>晋升阈值配置得当，让长时间存活的对象尽快晋升</li>
</ul>
<h4 id="老年代调优"><a href="#老年代调优" class="headerlink" title="老年代调优"></a>老年代调优</h4><h2 id="四、类加载与字节码技术"><a href="#四、类加载与字节码技术" class="headerlink" title="四、类加载与字节码技术"></a>四、类加载与字节码技术</h2><p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151300.png" srcset="/img/loading.gif" alt=""></p>
<h3 id="1、类文件结构"><a href="#1、类文件结构" class="headerlink" title="1、类文件结构"></a>1、类文件结构</h3><p>首先获得.class字节码文件</p>
<p>方法：</p>
<ul>
<li>在文本文档里写入java代码（文件名与类名一致），将文件类型改为.java</li>
<li>java终端中，执行javac X:...\XXX.java</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200910155135.png" srcset="/img/loading.gif" alt=""></p>
<p>以下是字节码文件</p>
<pre><code class="hljs angelscript"><span class="hljs-number">0000000</span> ca fe ba be <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">34</span> <span class="hljs-number">00</span> <span class="hljs-number">23</span> <span class="hljs-number">0</span>a <span class="hljs-number">00</span> <span class="hljs-number">06</span> <span class="hljs-number">00</span> <span class="hljs-number">15</span> <span class="hljs-number">09</span> 
<span class="hljs-number">0000020</span> <span class="hljs-number">00</span> <span class="hljs-number">16</span> <span class="hljs-number">00</span> <span class="hljs-number">17</span> <span class="hljs-number">08</span> <span class="hljs-number">00</span> <span class="hljs-number">18</span> <span class="hljs-number">0</span>a <span class="hljs-number">00</span> <span class="hljs-number">19</span> <span class="hljs-number">00</span> <span class="hljs-number">1</span>a <span class="hljs-number">07</span> <span class="hljs-number">00</span> <span class="hljs-number">1</span>b <span class="hljs-number">07</span> 
<span class="hljs-number">0000040</span> <span class="hljs-number">00</span> <span class="hljs-number">1</span>c <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">06</span> <span class="hljs-number">3</span>c <span class="hljs-number">69</span> <span class="hljs-number">6</span>e <span class="hljs-number">69</span> <span class="hljs-number">74</span> <span class="hljs-number">3</span>e <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">03</span> <span class="hljs-number">28</span> <span class="hljs-number">29</span> 
<span class="hljs-number">0000060</span> <span class="hljs-number">56</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">04</span> <span class="hljs-number">43</span> <span class="hljs-number">6f</span> <span class="hljs-number">64</span> <span class="hljs-number">65</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">0f</span> <span class="hljs-number">4</span>c <span class="hljs-number">69</span> <span class="hljs-number">6</span>e <span class="hljs-number">65</span> <span class="hljs-number">4</span>e 
<span class="hljs-number">0000100</span> <span class="hljs-number">75</span> <span class="hljs-number">6</span>d <span class="hljs-number">62</span> <span class="hljs-number">65</span> <span class="hljs-number">72</span> <span class="hljs-number">54</span> <span class="hljs-number">61</span> <span class="hljs-number">62</span> <span class="hljs-number">6</span>c <span class="hljs-number">65</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">12</span> <span class="hljs-number">4</span>c <span class="hljs-number">6f</span> <span class="hljs-number">63</span> 
<span class="hljs-number">0000120</span> <span class="hljs-number">61</span> <span class="hljs-number">6</span>c <span class="hljs-number">56</span> <span class="hljs-number">61</span> <span class="hljs-number">72</span> <span class="hljs-number">69</span> <span class="hljs-number">61</span> <span class="hljs-number">62</span> <span class="hljs-number">6</span>c <span class="hljs-number">65</span> <span class="hljs-number">54</span> <span class="hljs-number">61</span> <span class="hljs-number">62</span> <span class="hljs-number">6</span>c <span class="hljs-number">65</span> <span class="hljs-number">01</span> 
<span class="hljs-number">0000140</span> <span class="hljs-number">00</span> <span class="hljs-number">04</span> <span class="hljs-number">74</span> <span class="hljs-number">68</span> <span class="hljs-number">69</span> <span class="hljs-number">73</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">1</span>d <span class="hljs-number">4</span>c <span class="hljs-number">63</span> <span class="hljs-number">6</span>e <span class="hljs-number">2f</span> <span class="hljs-number">69</span> <span class="hljs-number">74</span> <span class="hljs-number">63</span> 
<span class="hljs-number">0000160</span> <span class="hljs-number">61</span> <span class="hljs-number">73</span> <span class="hljs-number">74</span> <span class="hljs-number">2f</span> <span class="hljs-number">6</span>a <span class="hljs-number">76</span> <span class="hljs-number">6</span>d <span class="hljs-number">2f</span> <span class="hljs-number">74</span> <span class="hljs-number">35</span> <span class="hljs-number">2f</span> <span class="hljs-number">48</span> <span class="hljs-number">65</span> <span class="hljs-number">6</span>c <span class="hljs-number">6</span>c <span class="hljs-number">6f</span> 
<span class="hljs-number">0000200</span> <span class="hljs-number">57</span> <span class="hljs-number">6f</span> <span class="hljs-number">72</span> <span class="hljs-number">6</span>c <span class="hljs-number">64</span> <span class="hljs-number">3</span>b <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">04</span> <span class="hljs-number">6</span>d <span class="hljs-number">61</span> <span class="hljs-number">69</span> <span class="hljs-number">6</span>e <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">16</span> 
<span class="hljs-number">0000220</span> <span class="hljs-number">28</span> <span class="hljs-number">5</span>b <span class="hljs-number">4</span>c <span class="hljs-number">6</span>a <span class="hljs-number">61</span> <span class="hljs-number">76</span> <span class="hljs-number">61</span> <span class="hljs-number">2f</span> <span class="hljs-number">6</span>c <span class="hljs-number">61</span> <span class="hljs-number">6</span>e <span class="hljs-number">67</span> <span class="hljs-number">2f</span> <span class="hljs-number">53</span> <span class="hljs-number">74</span> <span class="hljs-number">72</span> 
<span class="hljs-number">0000240</span> <span class="hljs-number">69</span> <span class="hljs-number">6</span>e <span class="hljs-number">67</span> <span class="hljs-number">3</span>b <span class="hljs-number">29</span> <span class="hljs-number">56</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">04</span> <span class="hljs-number">61</span> <span class="hljs-number">72</span> <span class="hljs-number">67</span> <span class="hljs-number">73</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">13</span> 
<span class="hljs-number">0000260</span> <span class="hljs-number">5</span>b <span class="hljs-number">4</span>c <span class="hljs-number">6</span>a <span class="hljs-number">61</span> <span class="hljs-number">76</span> <span class="hljs-number">61</span> <span class="hljs-number">2f</span> <span class="hljs-number">6</span>c <span class="hljs-number">61</span> <span class="hljs-number">6</span>e <span class="hljs-number">67</span> <span class="hljs-number">2f</span> <span class="hljs-number">53</span> <span class="hljs-number">74</span> <span class="hljs-number">72</span> <span class="hljs-number">69</span> 
<span class="hljs-number">0000300</span> <span class="hljs-number">6</span>e <span class="hljs-number">67</span> <span class="hljs-number">3</span>b <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">10</span> <span class="hljs-number">4</span>d <span class="hljs-number">65</span> <span class="hljs-number">74</span> <span class="hljs-number">68</span> <span class="hljs-number">6f</span> <span class="hljs-number">64</span> <span class="hljs-number">50</span> <span class="hljs-number">61</span> <span class="hljs-number">72</span> <span class="hljs-number">61</span> 
<span class="hljs-number">0000320</span> <span class="hljs-number">6</span>d <span class="hljs-number">65</span> <span class="hljs-number">74</span> <span class="hljs-number">65</span> <span class="hljs-number">72</span> <span class="hljs-number">73</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">0</span>a <span class="hljs-number">53</span> <span class="hljs-number">6f</span> <span class="hljs-number">75</span> <span class="hljs-number">72</span> <span class="hljs-number">63</span> <span class="hljs-number">65</span> <span class="hljs-number">46</span> 
<span class="hljs-number">0000340</span> <span class="hljs-number">69</span> <span class="hljs-number">6</span>c <span class="hljs-number">65</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">0f</span> <span class="hljs-number">48</span> <span class="hljs-number">65</span> <span class="hljs-number">6</span>c <span class="hljs-number">6</span>c <span class="hljs-number">6f</span> <span class="hljs-number">57</span> <span class="hljs-number">6f</span> <span class="hljs-number">72</span> <span class="hljs-number">6</span>c <span class="hljs-number">64</span>
<span class="hljs-number">0000360</span> <span class="hljs-number">2</span>e <span class="hljs-number">6</span>a <span class="hljs-number">61</span> <span class="hljs-number">76</span> <span class="hljs-number">61</span> <span class="hljs-number">0</span>c <span class="hljs-number">00</span> <span class="hljs-number">07</span> <span class="hljs-number">00</span> <span class="hljs-number">08</span> <span class="hljs-number">07</span> <span class="hljs-number">00</span> <span class="hljs-number">1</span>d <span class="hljs-number">0</span>c <span class="hljs-number">00</span> <span class="hljs-number">1</span>e 
<span class="hljs-number">0000400</span> <span class="hljs-number">00</span> <span class="hljs-number">1f</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">0</span>b <span class="hljs-number">68</span> <span class="hljs-number">65</span> <span class="hljs-number">6</span>c <span class="hljs-number">6</span>c <span class="hljs-number">6f</span> <span class="hljs-number">20</span> <span class="hljs-number">77</span> <span class="hljs-number">6f</span> <span class="hljs-number">72</span> <span class="hljs-number">6</span>c <span class="hljs-number">64</span> 
<span class="hljs-number">0000420</span> <span class="hljs-number">07</span> <span class="hljs-number">00</span> <span class="hljs-number">20</span> <span class="hljs-number">0</span>c <span class="hljs-number">00</span> <span class="hljs-number">21</span> <span class="hljs-number">00</span> <span class="hljs-number">22</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">1</span>b <span class="hljs-number">63</span> <span class="hljs-number">6</span>e <span class="hljs-number">2f</span> <span class="hljs-number">69</span> <span class="hljs-number">74</span> 
<span class="hljs-number">0000440</span> <span class="hljs-number">63</span> <span class="hljs-number">61</span> <span class="hljs-number">73</span> <span class="hljs-number">74</span> <span class="hljs-number">2f</span> <span class="hljs-number">6</span>a <span class="hljs-number">76</span> <span class="hljs-number">6</span>d <span class="hljs-number">2f</span> <span class="hljs-number">74</span> <span class="hljs-number">35</span> <span class="hljs-number">2f</span> <span class="hljs-number">48</span> <span class="hljs-number">65</span> <span class="hljs-number">6</span>c <span class="hljs-number">6</span>c 
<span class="hljs-number">0000460</span> <span class="hljs-number">6f</span> <span class="hljs-number">57</span> <span class="hljs-number">6f</span> <span class="hljs-number">72</span> <span class="hljs-number">6</span>c <span class="hljs-number">64</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">10</span> <span class="hljs-number">6</span>a <span class="hljs-number">61</span> <span class="hljs-number">76</span> <span class="hljs-number">61</span> <span class="hljs-number">2f</span> <span class="hljs-number">6</span>c <span class="hljs-number">61</span> 
<span class="hljs-number">0000500</span> <span class="hljs-number">6</span>e <span class="hljs-number">67</span> <span class="hljs-number">2f</span> <span class="hljs-number">4f</span> <span class="hljs-number">62</span> <span class="hljs-number">6</span>a <span class="hljs-number">65</span> <span class="hljs-number">63</span> <span class="hljs-number">74</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">10</span> <span class="hljs-number">6</span>a <span class="hljs-number">61</span> <span class="hljs-number">76</span> <span class="hljs-number">61</span> 
<span class="hljs-number">0000520</span> <span class="hljs-number">2f</span> <span class="hljs-number">6</span>c <span class="hljs-number">61</span> <span class="hljs-number">6</span>e <span class="hljs-number">67</span> <span class="hljs-number">2f</span> <span class="hljs-number">53</span> <span class="hljs-number">79</span> <span class="hljs-number">73</span> <span class="hljs-number">74</span> <span class="hljs-number">65</span> <span class="hljs-number">6</span>d <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">03</span> <span class="hljs-number">6f</span> 
<span class="hljs-number">0000540</span> <span class="hljs-number">75</span> <span class="hljs-number">74</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">15</span> <span class="hljs-number">4</span>c <span class="hljs-number">6</span>a <span class="hljs-number">61</span> <span class="hljs-number">76</span> <span class="hljs-number">61</span> <span class="hljs-number">2f</span> <span class="hljs-number">69</span> <span class="hljs-number">6f</span> <span class="hljs-number">2f</span> <span class="hljs-number">50</span> <span class="hljs-number">72</span> 
<span class="hljs-number">0000560</span> <span class="hljs-number">69</span> <span class="hljs-number">6</span>e <span class="hljs-number">74</span> <span class="hljs-number">53</span> <span class="hljs-number">74</span> <span class="hljs-number">72</span> <span class="hljs-number">65</span> <span class="hljs-number">61</span> <span class="hljs-number">6</span>d <span class="hljs-number">3</span>b <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">13</span> <span class="hljs-number">6</span>a <span class="hljs-number">61</span> <span class="hljs-number">76</span> 
<span class="hljs-number">0000600</span> <span class="hljs-number">61</span> <span class="hljs-number">2f</span> <span class="hljs-number">69</span> <span class="hljs-number">6f</span> <span class="hljs-number">2f</span> <span class="hljs-number">50</span> <span class="hljs-number">72</span> <span class="hljs-number">69</span> <span class="hljs-number">6</span>e <span class="hljs-number">74</span> <span class="hljs-number">53</span> <span class="hljs-number">74</span> <span class="hljs-number">72</span> <span class="hljs-number">65</span> <span class="hljs-number">61</span> <span class="hljs-number">6</span>d 
<span class="hljs-number">0000620</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">07</span> <span class="hljs-number">70</span> <span class="hljs-number">72</span> <span class="hljs-number">69</span> <span class="hljs-number">6</span>e <span class="hljs-number">74</span> <span class="hljs-number">6</span>c <span class="hljs-number">6</span>e <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">15</span> <span class="hljs-number">28</span> <span class="hljs-number">4</span>c <span class="hljs-number">6</span>a 
<span class="hljs-number">0000640</span> <span class="hljs-number">61</span> <span class="hljs-number">76</span> <span class="hljs-number">61</span> <span class="hljs-number">2f</span> <span class="hljs-number">6</span>c <span class="hljs-number">61</span> <span class="hljs-number">6</span>e <span class="hljs-number">67</span> <span class="hljs-number">2f</span> <span class="hljs-number">53</span> <span class="hljs-number">74</span> <span class="hljs-number">72</span> <span class="hljs-number">69</span> <span class="hljs-number">6</span>e <span class="hljs-number">67</span> <span class="hljs-number">3</span>b 
<span class="hljs-number">0000660</span> <span class="hljs-number">29</span> <span class="hljs-number">56</span> <span class="hljs-number">00</span> <span class="hljs-number">21</span> <span class="hljs-number">00</span> <span class="hljs-number">05</span> <span class="hljs-number">00</span> <span class="hljs-number">06</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">02</span> <span class="hljs-number">00</span> <span class="hljs-number">01</span> 
<span class="hljs-number">0000700</span> <span class="hljs-number">00</span> <span class="hljs-number">07</span> <span class="hljs-number">00</span> <span class="hljs-number">08</span> <span class="hljs-number">00</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">09</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">2f</span> <span class="hljs-number">00</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">01</span> 
<span class="hljs-number">0000720</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">05</span> <span class="hljs-number">2</span>a b7 <span class="hljs-number">00</span> <span class="hljs-number">01</span> b1 <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">02</span> <span class="hljs-number">00</span> <span class="hljs-number">0</span>a <span class="hljs-number">00</span> 
<span class="hljs-number">0000740</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">06</span> <span class="hljs-number">00</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">04</span> <span class="hljs-number">00</span> <span class="hljs-number">0</span>b <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">0</span>c <span class="hljs-number">00</span> 
<span class="hljs-number">0000760</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">05</span> <span class="hljs-number">00</span> <span class="hljs-number">0</span>c <span class="hljs-number">00</span> <span class="hljs-number">0</span>d <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">09</span> <span class="hljs-number">00</span> <span class="hljs-number">0</span>e <span class="hljs-number">00</span> 
<span class="hljs-number">0001000</span> <span class="hljs-number">0f</span> <span class="hljs-number">00</span> <span class="hljs-number">02</span> <span class="hljs-number">00</span> <span class="hljs-number">09</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">37</span> <span class="hljs-number">00</span> <span class="hljs-number">02</span> <span class="hljs-number">00</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> 
<span class="hljs-number">0001020</span> <span class="hljs-number">09</span> b2 <span class="hljs-number">00</span> <span class="hljs-number">02</span> <span class="hljs-number">12</span> <span class="hljs-number">03</span> b6 <span class="hljs-number">00</span> <span class="hljs-number">04</span> b1 <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">02</span> <span class="hljs-number">00</span> <span class="hljs-number">0</span>a 
<span class="hljs-number">0001040</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">0</span>a <span class="hljs-number">00</span> <span class="hljs-number">02</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">06</span> <span class="hljs-number">00</span> <span class="hljs-number">08</span> <span class="hljs-number">00</span> <span class="hljs-number">07</span> <span class="hljs-number">00</span> <span class="hljs-number">0</span>b 
<span class="hljs-number">0001060</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">0</span>c <span class="hljs-number">00</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">09</span> <span class="hljs-number">00</span> <span class="hljs-number">10</span> <span class="hljs-number">00</span> <span class="hljs-number">11</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> 
<span class="hljs-number">0001100</span> <span class="hljs-number">00</span> <span class="hljs-number">12</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">05</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">10</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">13</span> <span class="hljs-number">00</span> 
<span class="hljs-number">0001120</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">02</span> <span class="hljs-number">00</span> <span class="hljs-number">14</span></code></pre>



<p>根据 JVM 规范，<strong>类文件结构</strong>如下</p>
<pre><code class="hljs properties"><span class="hljs-attr">u4</span> 			 <span class="hljs-string">magic</span>
<span class="hljs-attr">u2</span> <span class="hljs-string">            minor_version;    </span>
<span class="hljs-attr">u2</span> <span class="hljs-string">            major_version;    </span>
<span class="hljs-attr">u2</span> <span class="hljs-string">            constant_pool_count;    </span>
<span class="hljs-attr">cp_info</span> <span class="hljs-string">       constant_pool[constant_pool_count-1];    </span>
<span class="hljs-attr">u2</span> <span class="hljs-string">            access_flags;    </span>
<span class="hljs-attr">u2</span> <span class="hljs-string">            this_class;    </span>
<span class="hljs-attr">u2</span> <span class="hljs-string">            super_class;   </span>
<span class="hljs-attr">u2</span> <span class="hljs-string">            interfaces_count;    </span>
<span class="hljs-attr">u2</span> <span class="hljs-string">            interfaces[interfaces_count];   </span>
<span class="hljs-attr">u2</span> <span class="hljs-string">            fields_count;    </span>
<span class="hljs-attr">field_info</span> <span class="hljs-string">    fields[fields_count];   </span>
<span class="hljs-attr">u2</span> <span class="hljs-string">            methods_count;    </span>
<span class="hljs-attr">method_info</span> <span class="hljs-string">   methods[methods_count];    </span>
<span class="hljs-attr">u2</span> <span class="hljs-string">            attributes_count;    </span>
<span class="hljs-attr">attribute_info</span> <span class="hljs-string">attributes[attributes_count];</span></code></pre>



<h4 id="魔数"><a href="#魔数" class="headerlink" title="魔数"></a>魔数</h4><p>u4 magic </p>
<p>对应字节码文件的0~3个字节</p>
<p>0000000 <strong>ca fe ba be</strong> 00 00 00 34 00 23 0a 00 06 00 15 09</p>
<h4 id="版本"><a href="#版本" class="headerlink" title="版本"></a>版本</h4><p>u2             minor_version;    </p>
<p>u2             major_version;</p>
<p>0000000 ca fe ba be <strong>00 00 00 34</strong> 00 23 0a 00 06 00 15 09 </p>
<p>34H = 52，代表JDK8</p>
<h4 id="常量池-1"><a href="#常量池-1" class="headerlink" title="常量池"></a>常量池</h4><p>见资料文件</p>
<p>…略</p>
<h3 id="2、字节码指令"><a href="#2、字节码指令" class="headerlink" title="2、字节码指令"></a>2、字节码指令</h3><p>可参考 </p>
<p><a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5" target="_blank" rel="noopener">https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5</a></p>
<h4 id="javap工具"><a href="#javap工具" class="headerlink" title="javap工具"></a>javap工具</h4><p>Oracle 提供了 <strong>javap</strong> 工具来反编译 class 文件</p>
<pre><code class="hljs taggerscript">javap -v F:<span class="hljs-symbol">\T</span>hread_study<span class="hljs-symbol">\s</span>rc<span class="hljs-symbol">\c</span>om<span class="hljs-symbol">\n</span>yima<span class="hljs-symbol">\J</span>VM<span class="hljs-symbol">\d</span>ay01<span class="hljs-symbol">\M</span>ain.class</code></pre>



<pre><code class="hljs lasso">F:\Thread_study&gt;javap <span class="hljs-params">-v</span> F:\Thread_study\src\com\nyima\JVM\day5\Demo1.class
Classfile /F:/Thread_study/src/com/nyima/JVM/day5/Demo1.class
  Last modified <span class="hljs-number">2020</span><span class="hljs-number">-6</span><span class="hljs-number">-6</span>; size <span class="hljs-number">434</span> <span class="hljs-built_in">bytes</span>
  MD5 checksum df1dce65bf6fb0b4c1de318051f4a67e
  Compiled from <span class="hljs-string">"Demo1.java"</span>
<span class="hljs-keyword">public</span> class com.nyima.JVM.day5.Demo1
  minor version: <span class="hljs-number">0</span>
  major version: <span class="hljs-number">52</span>
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#15         <span class="hljs-comment">// java/lang/Object."&lt;init&gt;":()V</span>
   #2 = Fieldref           #16.#17        <span class="hljs-comment">// java/lang/System.out:Ljava/io/PrintStream;</span>
   #3 = <span class="hljs-built_in">String</span>             #18            <span class="hljs-comment">// hello world</span>
   #4 = Methodref          #19.#20        <span class="hljs-comment">// java/io/PrintStream.println:(Ljava/lang/String;)V</span>
   #5 = Class              #21            <span class="hljs-comment">// com/nyima/JVM/day5/Demo1</span>
   #6 = Class              #22            <span class="hljs-comment">// java/lang/Object</span>
   #7 = Utf8               &lt;init&gt;
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               main
  #12 = Utf8               (<span class="hljs-meta">[</span>Ljava/lang/<span class="hljs-built_in">String</span>;)V
  #13 = Utf8               SourceFile
  #14 = Utf8               Demo1.java
  #15 = NameAndType        #7:#8          <span class="hljs-comment">// "&lt;init&gt;":()V</span>
  #16 = Class              #23            <span class="hljs-comment">// java/lang/System</span>
  #17 = NameAndType        #24:#25        <span class="hljs-comment">// out:Ljava/io/PrintStream;</span>
  #18 = Utf8               hello world
  #19 = Class              #26            <span class="hljs-comment">// java/io/PrintStream</span>
  #20 = NameAndType        #27:#28        <span class="hljs-comment">// println:(Ljava/lang/String;)V</span>
  #21 = Utf8               com/nyima/JVM/day5/Demo1
  #22 = Utf8               java/lang/Object
  #23 = Utf8               java/lang/System
  #24 = Utf8               out
  #25 = Utf8               Ljava/io/PrintStream;
  #26 = Utf8               java/io/PrintStream
  #27 = Utf8               println
  #28 = Utf8               (Ljava/lang/<span class="hljs-built_in">String</span>;)V
&#123;
  <span class="hljs-keyword">public</span> com.nyima.JVM.day5.Demo1();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      <span class="hljs-built_in">stack</span>=<span class="hljs-number">1</span>, locals=<span class="hljs-number">1</span>, args_size=<span class="hljs-number">1</span>
         <span class="hljs-number">0</span>: aload_0
         <span class="hljs-number">1</span>: invokespecial #1                  <span class="hljs-comment">// Method java/lang/Object."&lt;init&gt;":()V</span>
         <span class="hljs-number">4</span>: <span class="hljs-keyword">return</span>
      LineNumberTable:
        line <span class="hljs-number">7</span>: <span class="hljs-number">0</span>

  <span class="hljs-keyword">public</span> static <span class="hljs-literal">void</span> main(java.lang.<span class="hljs-built_in">String</span><span class="hljs-meta">[</span><span class="hljs-meta">]</span>);
    descriptor: (<span class="hljs-meta">[</span>Ljava/lang/<span class="hljs-built_in">String</span>;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      <span class="hljs-built_in">stack</span>=<span class="hljs-number">2</span>, locals=<span class="hljs-number">1</span>, args_size=<span class="hljs-number">1</span>
         <span class="hljs-number">0</span>: getstatic     #2                  <span class="hljs-comment">// Field java/lang/System.out:Ljava/io/PrintStream;</span>
         <span class="hljs-number">3</span>: ldc           #3                  <span class="hljs-comment">// String hello world</span>
         <span class="hljs-number">5</span>: invokevirtual #4                  <span class="hljs-comment">// Method java/io/PrintStream.println:(Ljava/lang/String;)V</span>

         <span class="hljs-number">8</span>: <span class="hljs-keyword">return</span>
      LineNumberTable:
        line <span class="hljs-number">9</span>: <span class="hljs-number">0</span>
        line <span class="hljs-number">10</span>: <span class="hljs-number">8</span>
&#125;</code></pre>



<h4 id="图解方法执行流程"><a href="#图解方法执行流程" class="headerlink" title="图解方法执行流程"></a>图解方法执行流程</h4><p>代码</p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo3_1</span> </span>&#123;    
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;        
		<span class="hljs-keyword">int</span> a = <span class="hljs-number">10</span>;        
		<span class="hljs-keyword">int</span> b = Short.MAX_VALUE + <span class="hljs-number">1</span>;        
		<span class="hljs-keyword">int</span> c = a + b;        
		System.out.println(c);   
    &#125; 
&#125;</code></pre>



<p><strong>常量池载入运行时常量池</strong></p>
<p>常量池也属于方法区，只不过这里单独提出来了</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151317.png" srcset="/img/loading.gif" alt=""></p>
<p><strong>方法字节码载入方法区</strong></p>
<p>（stack=2，locals=4） 对应操作数栈有2个空间（每个空间4个字节），局部变量表中有4个槽位</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151325.png" srcset="/img/loading.gif" alt=""></p>
<p><strong>执行引擎开始执行字节码</strong></p>
<p><strong>bipush 10</strong> </p>
<ul>
<li><strong>将一个 byte 压入操作数栈</strong>（其长度会补齐 4 个字节），类似的指令还有 <ul>
<li>sipush 将一个 short 压入操作数栈（其长度会补齐 4 个字节）</li>
<li>ldc 将一个 int 压入操作数栈 </li>
<li>ldc2_w 将一个 long 压入操作数栈（<strong>分两次压入</strong>，因为 long 是 8 个字节）</li>
<li>这里小的数字都是和字节码指令存在一起，<strong>超过 short 范围的数字存入了常量池</strong></li>
</ul>
</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151336.png" srcset="/img/loading.gif" alt=""></p>
<p><strong>istore 1</strong></p>
<p>将操作数栈栈顶元素弹出，放入局部变量表的slot 1中</p>
<p>对应代码中的 </p>
<pre><code class="hljs java">a = <span class="hljs-number">10</span></code></pre>

<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151346.png" srcset="/img/loading.gif" alt=""></p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151412.png" srcset="/img/loading.gif" alt=""></p>
<p><strong>ldc #3</strong></p>
<p>读取运行时常量池中#3，即32768(超过short最大值范围的数会被放到运行时常量池中)，将其加载到操作数栈中</p>
<p>注意 Short.MAX_VALUE 是 32767，所以 32768 = Short.MAX_VALUE + 1 实际是在编译期间计算好的</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151421.png" srcset="/img/loading.gif" alt=""></p>
<p><strong>istore 2</strong></p>
<p>将操作数栈中的元素弹出，放到局部变量表的2号位置</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151432.png" srcset="/img/loading.gif" alt=""></p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151441.png" srcset="/img/loading.gif" alt=""></p>
<p><strong>iload1 iload2</strong></p>
<p>将局部变量表中1号位置和2号位置的元素放入操作数栈中</p>
<ul>
<li>因为只能在操作数栈中执行运算操作</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151450.png" srcset="/img/loading.gif" alt=""></p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151459.png" srcset="/img/loading.gif" alt=""></p>
<p><strong>iadd</strong></p>
<p>将操作数栈中的两个元素<strong>弹出栈</strong>并相加，结果在压入操作数栈中</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151508.png" srcset="/img/loading.gif" alt=""></p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151523.png" srcset="/img/loading.gif" alt=""></p>
<p><strong>istore 3</strong></p>
<p>将操作数栈中的元素弹出，放入局部变量表的3号位置</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151547.png" srcset="/img/loading.gif" alt=""></p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151555.png" srcset="/img/loading.gif" alt=""></p>
<p><strong>getstatic #4</strong></p>
<p>在运行时常量池中找到#4，发现是一个对象</p>
<p>在堆内存中找到该对象，并将其<strong>引用</strong>放入操作数栈中</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151605.png" srcset="/img/loading.gif" alt=""></p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151613.png" srcset="/img/loading.gif" alt=""></p>
<p><strong>iload 3</strong></p>
<p>将局部变量表中3号位置的元素压入操作数栈中</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151624.png" srcset="/img/loading.gif" alt=""></p>
<p><strong>invokevirtual 5</strong></p>
<p>找到常量池 #5 项，定位到方法区 java/io/PrintStream.println:(I)V 方法 </p>
<p>生成新的栈帧（分配 locals、stack等） </p>
<p>传递参数，执行新栈帧中的字节码</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151632.png" srcset="/img/loading.gif" alt=""></p>
<p>执行完毕，弹出栈帧 </p>
<p>清除 main 操作数栈内容</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608151640.png" srcset="/img/loading.gif" alt=""></p>
<p><strong>return</strong><br>完成 main 方法调用，弹出 main 栈帧，程序结束</p>
<h4 id="通过字节码指令来分析问题"><a href="#通过字节码指令来分析问题" class="headerlink" title="通过字节码指令来分析问题"></a>通过字节码指令来分析问题</h4><p>代码</p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo2</span> </span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		<span class="hljs-keyword">int</span> i=<span class="hljs-number">0</span>;
		<span class="hljs-keyword">int</span> x=<span class="hljs-number">0</span>;
		<span class="hljs-keyword">while</span>(i&lt;<span class="hljs-number">10</span>) &#123;
			x = x++;
			i++;
		&#125;
		System.out.println(x); <span class="hljs-comment">//接过为0</span>
	&#125;
&#125;</code></pre>

<p>为什么最终的x结果为0呢？ 通过分析字节码指令即可知晓</p>
<pre><code class="hljs java">Code:
     stack=<span class="hljs-number">2</span>, locals=<span class="hljs-number">3</span>, args_size=<span class="hljs-number">1</span>	<span class="hljs-comment">//操作数栈分配2个空间，局部变量表分配3个空间</span>
        <span class="hljs-number">0</span>: iconst_0	<span class="hljs-comment">//准备一个常数0</span>
        <span class="hljs-number">1</span>: istore_1	<span class="hljs-comment">//将常数0放入局部变量表的1号槽位 i=0</span>
        <span class="hljs-number">2</span>: iconst_0	<span class="hljs-comment">//准备一个常数0</span>
        <span class="hljs-number">3</span>: istore_2	<span class="hljs-comment">//将常数0放入局部变量的2号槽位 x=0	</span>
        <span class="hljs-number">4</span>: iload_1		<span class="hljs-comment">//将局部变量表1号槽位的数放入操作数栈中</span>
        <span class="hljs-number">5</span>: bipush        <span class="hljs-number">10</span>	<span class="hljs-comment">//将数字10放入操作数栈中，此时操作数栈中有2个数</span>
        <span class="hljs-number">7</span>: if_icmpge     <span class="hljs-number">21</span>	<span class="hljs-comment">//比较操作数栈中的两个数，如果下面的数大于上面的数，就跳转到21。这里的比较是将两个数做减法。因为涉及运算操作，所以会将两个数弹出操作数栈来进行运算。运算结束后操作数栈为空</span>
       <span class="hljs-number">10</span>: iload_2		<span class="hljs-comment">//将局部变量2号槽位的数放入操作数栈中，放入的值是0</span>
       <span class="hljs-number">11</span>: iinc          <span class="hljs-number">2</span>, <span class="hljs-number">1</span>	<span class="hljs-comment">//将局部变量2号槽位的数加1，自增后，槽位中的值为1</span>
       <span class="hljs-number">14</span>: istore_2	<span class="hljs-comment">//将操作数栈中的数放入到局部变量表的2号槽位，2号槽位的值又变为了0</span>
       <span class="hljs-number">15</span>: iinc          <span class="hljs-number">1</span>, <span class="hljs-number">1</span> <span class="hljs-comment">//1号槽位的值自增1</span>
       <span class="hljs-number">18</span>: goto          <span class="hljs-number">4</span> <span class="hljs-comment">//跳转到第4条指令</span>
       21: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       <span class="hljs-number">24</span>: iload_2
       25: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
       <span class="hljs-number">28</span>: <span class="hljs-keyword">return</span></code></pre>





<h4 id="构造方法"><a href="#构造方法" class="headerlink" title="构造方法"></a>构造方法</h4><h5 id="cinit-V"><a href="#cinit-V" class="headerlink" title="cinit()V"></a>cinit()V</h5><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo3</span> </span>&#123;
	<span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> i = <span class="hljs-number">10</span>;

	<span class="hljs-keyword">static</span> &#123;
		i = <span class="hljs-number">20</span>;
	&#125;

	<span class="hljs-keyword">static</span> &#123;
		i = <span class="hljs-number">30</span>;
	&#125;

	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		System.out.println(i); <span class="hljs-comment">//结果为30</span>
	&#125;
&#125;</code></pre>



<p>编译器会按<strong>从上至下</strong>的顺序，收集所有 static 静态代码块和静态成员赋值的代码，<strong>合并</strong>为一个特殊的方法 cinit()V ：</p>
<pre><code class="hljs angelscript">stack=<span class="hljs-number">1</span>, locals=<span class="hljs-number">0</span>, args_size=<span class="hljs-number">0</span>
         <span class="hljs-number">0</span>: bipush        <span class="hljs-number">10</span>
         <span class="hljs-number">2</span>: putstatic     #<span class="hljs-number">3</span>                  <span class="hljs-comment">// Field i:I</span>
         <span class="hljs-number">5</span>: bipush        <span class="hljs-number">20</span>
         <span class="hljs-number">7</span>: putstatic     #<span class="hljs-number">3</span>                  <span class="hljs-comment">// Field i:I</span>
        <span class="hljs-number">10</span>: bipush        <span class="hljs-number">30</span>
        <span class="hljs-number">12</span>: putstatic     #<span class="hljs-number">3</span>                  <span class="hljs-comment">// Field i:I</span>
        <span class="hljs-number">15</span>: <span class="hljs-keyword">return</span></code></pre>



<h5 id="init-V"><a href="#init-V" class="headerlink" title="init()V"></a>init()V</h5><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo4</span> </span>&#123;
	<span class="hljs-keyword">private</span> String a = <span class="hljs-string">"s1"</span>;

	&#123;
		b = <span class="hljs-number">20</span>;
	&#125;

	<span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> b = <span class="hljs-number">10</span>;

	&#123;
		a = <span class="hljs-string">"s2"</span>;
	&#125;

	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Demo4</span><span class="hljs-params">(String a, <span class="hljs-keyword">int</span> b)</span> </span>&#123;
		<span class="hljs-keyword">this</span>.a = a;
		<span class="hljs-keyword">this</span>.b = b;
	&#125;

	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		Demo4 d = <span class="hljs-keyword">new</span> Demo4(<span class="hljs-string">"s3"</span>, <span class="hljs-number">30</span>);
		System.out.println(d.a);
		System.out.println(d.b);
	&#125;
&#125;</code></pre>

<p>编译器会按<strong>从上至下</strong>的顺序，收集所有 {} 代码块和成员变量赋值的代码，<strong>形成新的构造方法</strong>，但<strong>原始构造方法</strong>内的代码<strong>总是在后</strong></p>
<pre><code class="hljs angelscript">Code:
     stack=<span class="hljs-number">2</span>, locals=<span class="hljs-number">3</span>, args_size=<span class="hljs-number">3</span>
        <span class="hljs-number">0</span>: aload_0
        <span class="hljs-number">1</span>: invokespecial #<span class="hljs-number">1</span>                  <span class="hljs-comment">// Method java/lang/Object."&lt;init&gt;":()V</span>
        <span class="hljs-number">4</span>: aload_0
        <span class="hljs-number">5</span>: ldc           #<span class="hljs-number">2</span>                  <span class="hljs-comment">// String s1</span>
        <span class="hljs-number">7</span>: putfield      #<span class="hljs-number">3</span>                  <span class="hljs-comment">// Field a:Ljava/lang/String;</span>
       <span class="hljs-number">10</span>: aload_0
       <span class="hljs-number">11</span>: bipush        <span class="hljs-number">20</span>
       <span class="hljs-number">13</span>: putfield      #<span class="hljs-number">4</span>                  <span class="hljs-comment">// Field b:I</span>
       <span class="hljs-number">16</span>: aload_0
       <span class="hljs-number">17</span>: bipush        <span class="hljs-number">10</span>
       <span class="hljs-number">19</span>: putfield      #<span class="hljs-number">4</span>                  <span class="hljs-comment">// Field b:I</span>
       <span class="hljs-number">22</span>: aload_0
       <span class="hljs-number">23</span>: ldc           #<span class="hljs-number">5</span>                  <span class="hljs-comment">// String s2</span>
       <span class="hljs-number">25</span>: putfield      #<span class="hljs-number">3</span>                  <span class="hljs-comment">// Field a:Ljava/lang/String;</span>
       <span class="hljs-comment">//原始构造方法在最后执行</span>
       <span class="hljs-number">28</span>: aload_0
       <span class="hljs-number">29</span>: aload_1
       <span class="hljs-number">30</span>: putfield      #<span class="hljs-number">3</span>                  <span class="hljs-comment">// Field a:Ljava/lang/String;</span>
       <span class="hljs-number">33</span>: aload_0
       <span class="hljs-number">34</span>: iload_2
       <span class="hljs-number">35</span>: putfield      #<span class="hljs-number">4</span>                  <span class="hljs-comment">// Field b:I</span>
       <span class="hljs-number">38</span>: <span class="hljs-keyword">return</span></code></pre>



<h4 id="方法调用"><a href="#方法调用" class="headerlink" title="方法调用"></a>方法调用</h4><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo5</span> </span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Demo5</span><span class="hljs-params">()</span> </span>&#123;

	&#125;

	<span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">test1</span><span class="hljs-params">()</span> </span>&#123;

	&#125;

	<span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">void</span> <span class="hljs-title">test2</span><span class="hljs-params">()</span> </span>&#123;

	&#125;

	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">test3</span><span class="hljs-params">()</span> </span>&#123;

	&#125;

	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">test4</span><span class="hljs-params">()</span> </span>&#123;

	&#125;

	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		Demo5 demo5 = <span class="hljs-keyword">new</span> Demo5();
		demo5.test1();
		demo5.test2();
		demo5.test3();
		Demo5.test4();
	&#125;
&#125;</code></pre>

<p>不同方法在调用时，对应的虚拟机指令有所区别</p>
<ul>
<li>私有、构造、被final修饰的方法，在调用时都使用<strong>invokespecial</strong>指令</li>
<li>普通成员方法在调用时，使用invokespecial指令。因为编译期间无法确定该方法的内容，只有在运行期间才能确定</li>
<li>静态方法在调用时使用invokestatic指令</li>
</ul>
<pre><code class="hljs angelscript">Code:
      stack=<span class="hljs-number">2</span>, locals=<span class="hljs-number">2</span>, args_size=<span class="hljs-number">1</span>
         <span class="hljs-number">0</span>: new           #<span class="hljs-number">2</span>                  <span class="hljs-comment">// class com/nyima/JVM/day5/Demo5 </span>
         <span class="hljs-number">3</span>: dup
         <span class="hljs-number">4</span>: invokespecial #<span class="hljs-number">3</span>                  <span class="hljs-comment">// Method "&lt;init&gt;":()V</span>
         <span class="hljs-number">7</span>: astore_1
         <span class="hljs-number">8</span>: aload_1
         <span class="hljs-number">9</span>: invokespecial #<span class="hljs-number">4</span>                  <span class="hljs-comment">// Method test1:()V</span>
        <span class="hljs-number">12</span>: aload_1
        <span class="hljs-number">13</span>: invokespecial #<span class="hljs-number">5</span>                  <span class="hljs-comment">// Method test2:()V</span>
        <span class="hljs-number">16</span>: aload_1
        <span class="hljs-number">17</span>: invokevirtual #<span class="hljs-number">6</span>                  <span class="hljs-comment">// Method test3:()V</span>
        <span class="hljs-number">20</span>: invokestatic  #<span class="hljs-number">7</span>                  <span class="hljs-comment">// Method test4:()V</span>
        <span class="hljs-number">23</span>: <span class="hljs-keyword">return</span></code></pre>

<ul>
<li>new 是创建【对象】，给对象分配堆内存，执行成功会将【<strong>对象引用</strong>】压入操作数栈 </li>
<li>dup 是赋值操作数栈栈顶的内容，本例即为【<strong>对象引用</strong>】，为什么需要两份引用呢，一个是要配合 invokespecial 调用该对象的构造方法 “init”:()V （会消耗掉栈顶一个引用），另一个要 配合 astore_1 赋值给局部变量 </li>
<li>终方法（ﬁnal），私有方法（private），构造方法都是由 invokespecial 指令来调用，属于静态绑定 </li>
<li>普通成员方法是由 invokevirtual 调用，属于<strong>动态绑定</strong>，即支持多态 成员方法与静态方法调用的另一个区别是，执行方法前是否需要【对象引用】 </li>
</ul>
<h4 id="多态原理"><a href="#多态原理" class="headerlink" title="多态原理"></a>多态原理</h4><p>因为普通成员方法需要在运行时才能确定具体的内容，所以虚拟机需要调用<strong>invokevirtual</strong>指令</p>
<p>在执行invokevirtual指令时，经历了以下几个步骤</p>
<ul>
<li>先通过栈帧中对象的引用找到对象</li>
<li>分析对象头，找到对象实际的Class</li>
<li>Class结构中有<strong>vtable</strong></li>
<li>查询vtable找到方法的具体地址</li>
<li>执行方法的字节码</li>
</ul>
<h4 id="异常处理"><a href="#异常处理" class="headerlink" title="异常处理"></a>异常处理</h4><h5 id="try-catch"><a href="#try-catch" class="headerlink" title="try-catch"></a>try-catch</h5><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo1</span> </span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>;
		<span class="hljs-keyword">try</span> &#123;
			i = <span class="hljs-number">10</span>;
		&#125;<span class="hljs-keyword">catch</span> (Exception e) &#123;
			i = <span class="hljs-number">20</span>;
		&#125;
	&#125;
&#125;</code></pre>

<p>对应字节码指令</p>
<pre><code class="hljs angelscript">Code:
     stack=<span class="hljs-number">1</span>, locals=<span class="hljs-number">3</span>, args_size=<span class="hljs-number">1</span>
        <span class="hljs-number">0</span>: iconst_0
        <span class="hljs-number">1</span>: istore_1
        <span class="hljs-number">2</span>: bipush        <span class="hljs-number">10</span>
        <span class="hljs-number">4</span>: istore_1
        <span class="hljs-number">5</span>: goto          <span class="hljs-number">12</span>
        <span class="hljs-number">8</span>: astore_2
        <span class="hljs-number">9</span>: bipush        <span class="hljs-number">20</span>
       <span class="hljs-number">11</span>: istore_1
       <span class="hljs-number">12</span>: <span class="hljs-keyword">return</span>
     <span class="hljs-comment">//多出来一个异常表</span>
     Exception table:
        <span class="hljs-keyword">from</span>    to  target type
            <span class="hljs-number">2</span>     <span class="hljs-number">5</span>     <span class="hljs-number">8</span>   Class java/lang/Exception</code></pre>

<ul>
<li>可以看到多出来一个 Exception table 的结构，[from, to) 是<strong>前闭后开</strong>（也就是检测2~4行）的检测范围，一旦这个范围内的字节码执行出现异常，则通过 type 匹配异常类型，如果一致，进入 target 所指示行号 </li>
<li>8行的字节码指令 astore_2 是将异常对象引用存入局部变量表的2号位置（为e）</li>
</ul>
<h5 id="多个single-catch"><a href="#多个single-catch" class="headerlink" title="多个single-catch"></a>多个single-catch</h5><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo1</span> </span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>;
		<span class="hljs-keyword">try</span> &#123;
			i = <span class="hljs-number">10</span>;
		&#125;<span class="hljs-keyword">catch</span> (ArithmeticException e) &#123;
			i = <span class="hljs-number">20</span>;
		&#125;<span class="hljs-keyword">catch</span> (Exception e) &#123;
			i = <span class="hljs-number">30</span>;
		&#125;
	&#125;
&#125;</code></pre>

<p>对应的字节码</p>
<pre><code class="hljs angelscript">Code:
     stack=<span class="hljs-number">1</span>, locals=<span class="hljs-number">3</span>, args_size=<span class="hljs-number">1</span>
        <span class="hljs-number">0</span>: iconst_0
        <span class="hljs-number">1</span>: istore_1
        <span class="hljs-number">2</span>: bipush        <span class="hljs-number">10</span>
        <span class="hljs-number">4</span>: istore_1
        <span class="hljs-number">5</span>: goto          <span class="hljs-number">19</span>
        <span class="hljs-number">8</span>: astore_2
        <span class="hljs-number">9</span>: bipush        <span class="hljs-number">20</span>
       <span class="hljs-number">11</span>: istore_1
       <span class="hljs-number">12</span>: goto          <span class="hljs-number">19</span>
       <span class="hljs-number">15</span>: astore_2
       <span class="hljs-number">16</span>: bipush        <span class="hljs-number">30</span>
       <span class="hljs-number">18</span>: istore_1
       <span class="hljs-number">19</span>: <span class="hljs-keyword">return</span>
     Exception table:
        <span class="hljs-keyword">from</span>    to  target type
            <span class="hljs-number">2</span>     <span class="hljs-number">5</span>     <span class="hljs-number">8</span>   Class java/lang/ArithmeticException
            <span class="hljs-number">2</span>     <span class="hljs-number">5</span>    <span class="hljs-number">15</span>   Class java/lang/Exception</code></pre>

<ul>
<li>因为异常出现时，<strong>只能进入</strong> Exception table 中<strong>一个分支</strong>，所以局部变量表 slot 2 位置<strong>被共用</strong></li>
</ul>
<h5 id="finally"><a href="#finally" class="headerlink" title="finally"></a>finally</h5><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo2</span> </span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>;
		<span class="hljs-keyword">try</span> &#123;
			i = <span class="hljs-number">10</span>;
		&#125; <span class="hljs-keyword">catch</span> (Exception e) &#123;
			i = <span class="hljs-number">20</span>;
		&#125; <span class="hljs-keyword">finally</span> &#123;
			i = <span class="hljs-number">30</span>;
		&#125;
	&#125;
&#125;</code></pre>

<p>对应字节码</p>
<pre><code class="hljs java">Code:
     stack=<span class="hljs-number">1</span>, locals=<span class="hljs-number">4</span>, args_size=<span class="hljs-number">1</span>
        <span class="hljs-number">0</span>: iconst_0
        <span class="hljs-number">1</span>: istore_1
        <span class="hljs-comment">//try块</span>
        <span class="hljs-number">2</span>: bipush        <span class="hljs-number">10</span>
        <span class="hljs-number">4</span>: istore_1
        <span class="hljs-comment">//try块执行完后，会执行finally    </span>
        <span class="hljs-number">5</span>: bipush        <span class="hljs-number">30</span>
        <span class="hljs-number">7</span>: istore_1
        <span class="hljs-number">8</span>: goto          <span class="hljs-number">27</span>
       <span class="hljs-comment">//catch块     </span>
       <span class="hljs-number">11</span>: astore_2 <span class="hljs-comment">//异常信息放入局部变量表的2号槽位</span>
       <span class="hljs-number">12</span>: bipush        <span class="hljs-number">20</span>
       <span class="hljs-number">14</span>: istore_1
       <span class="hljs-comment">//catch块执行完后，会执行finally        </span>
       <span class="hljs-number">15</span>: bipush        <span class="hljs-number">30</span>
       <span class="hljs-number">17</span>: istore_1
       <span class="hljs-number">18</span>: goto          <span class="hljs-number">27</span>
       <span class="hljs-comment">//出现异常，但未被Exception捕获，会抛出其他异常，这时也需要执行finally块中的代码   </span>
       <span class="hljs-number">21</span>: astore_3
       <span class="hljs-number">22</span>: bipush        <span class="hljs-number">30</span>
       <span class="hljs-number">24</span>: istore_1
       <span class="hljs-number">25</span>: aload_3
       <span class="hljs-number">26</span>: athrow  <span class="hljs-comment">//抛出异常</span>
       <span class="hljs-number">27</span>: <span class="hljs-keyword">return</span>
     Exception table:
        from    to  target type
            <span class="hljs-number">2</span>     <span class="hljs-number">5</span>    <span class="hljs-number">11</span>   Class java/lang/Exception
            <span class="hljs-number">2</span>     <span class="hljs-number">5</span>    <span class="hljs-number">21</span>   any
           <span class="hljs-number">11</span>    <span class="hljs-number">15</span>    <span class="hljs-number">21</span>   any</code></pre>

<p>可以看到 ﬁnally 中的代码被<strong>复制了 3 份</strong>，分别放入 try 流程，catch 流程以及 catch剩余的异常类型流程 </p>
<p><strong>注意</strong>：虽然从字节码指令看来，每个块中都有finally块，但是finally块中的代码<strong>只会被执行一次</strong></p>
<h5 id="finally中的return"><a href="#finally中的return" class="headerlink" title="finally中的return"></a>finally中的return</h5><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo3</span> </span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		<span class="hljs-keyword">int</span> i = Demo3.test();
        <span class="hljs-comment">//结果为20</span>
		System.out.println(i);
	&#125;

	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">test</span><span class="hljs-params">()</span> </span>&#123;
		<span class="hljs-keyword">int</span> i;
		<span class="hljs-keyword">try</span> &#123;
			i = <span class="hljs-number">10</span>;
			<span class="hljs-keyword">return</span> i;
		&#125; <span class="hljs-keyword">finally</span> &#123;
			i = <span class="hljs-number">20</span>;
			<span class="hljs-keyword">return</span> i;
		&#125;
	&#125;
&#125;</code></pre>

<p>对应字节码</p>
<pre><code class="hljs java">Code:
     stack=<span class="hljs-number">1</span>, locals=<span class="hljs-number">3</span>, args_size=<span class="hljs-number">0</span>
        <span class="hljs-number">0</span>: bipush        <span class="hljs-number">10</span>
        <span class="hljs-number">2</span>: istore_0
        <span class="hljs-number">3</span>: iload_0
        <span class="hljs-number">4</span>: istore_1  <span class="hljs-comment">//暂存返回值</span>
        <span class="hljs-number">5</span>: bipush        <span class="hljs-number">20</span>
        <span class="hljs-number">7</span>: istore_0
        <span class="hljs-number">8</span>: iload_0
        <span class="hljs-number">9</span>: ireturn	<span class="hljs-comment">//ireturn会返回操作数栈顶的整型值20</span>
       <span class="hljs-comment">//如果出现异常，还是会执行finally块中的内容，没有抛出异常</span>
       <span class="hljs-number">10</span>: astore_2
       <span class="hljs-number">11</span>: bipush        <span class="hljs-number">20</span>
       <span class="hljs-number">13</span>: istore_0
       <span class="hljs-number">14</span>: iload_0
       <span class="hljs-number">15</span>: ireturn	<span class="hljs-comment">//这里没有athrow了，也就是如果在finally块中如果有返回操作的话，且try块中出现异常，会吞掉异常！</span>
     Exception table:
        from    to  target type
            <span class="hljs-number">0</span>     <span class="hljs-number">5</span>    <span class="hljs-number">10</span>   any</code></pre>

<ul>
<li>由于 ﬁnally 中的 <strong>ireturn</strong> 被插入了所有可能的流程，因此返回结果肯定以ﬁnally的为准 </li>
<li>至于字节码中第 2 行，似乎没啥用，且留个伏笔，看下个例子 </li>
<li>跟上例中的 ﬁnally 相比，发现<strong>没有 athrow 了</strong>，这告诉我们：如果在 ﬁnally 中出现了 return，会<strong>吞掉异常</strong></li>
<li>所以<strong>不要在finally中进行返回操作</strong></li>
</ul>
<h5 id="被吞掉的异常"><a href="#被吞掉的异常" class="headerlink" title="被吞掉的异常"></a>被吞掉的异常</h5><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo3</span> </span>&#123;
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
      <span class="hljs-keyword">int</span> i = Demo3.test();
      <span class="hljs-comment">//最终结果为20</span>
      System.out.println(i);
   &#125;

   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">test</span><span class="hljs-params">()</span> </span>&#123;
      <span class="hljs-keyword">int</span> i;
      <span class="hljs-keyword">try</span> &#123;
         i = <span class="hljs-number">10</span>;
         <span class="hljs-comment">//这里应该会抛出异常</span>
         i = i/<span class="hljs-number">0</span>;
         <span class="hljs-keyword">return</span> i;
      &#125; <span class="hljs-keyword">finally</span> &#123;
         i = <span class="hljs-number">20</span>;
         <span class="hljs-keyword">return</span> i;
      &#125;
   &#125;
&#125;</code></pre>

<p>会发现打印结果为20，并未抛出异常</p>
<h5 id="finally不带return"><a href="#finally不带return" class="headerlink" title="finally不带return"></a>finally不带return</h5><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo4</span> </span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		<span class="hljs-keyword">int</span> i = Demo4.test();
		System.out.println(i);
	&#125;

	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">test</span><span class="hljs-params">()</span> </span>&#123;
		<span class="hljs-keyword">int</span> i = <span class="hljs-number">10</span>;
		<span class="hljs-keyword">try</span> &#123;
			<span class="hljs-keyword">return</span> i;
		&#125; <span class="hljs-keyword">finally</span> &#123;
			i = <span class="hljs-number">20</span>;
		&#125;
	&#125;
&#125;</code></pre>

<p>对应字节码</p>
<pre><code class="hljs java">Code:
     stack=<span class="hljs-number">1</span>, locals=<span class="hljs-number">3</span>, args_size=<span class="hljs-number">0</span>
        <span class="hljs-number">0</span>: bipush        <span class="hljs-number">10</span>
        <span class="hljs-number">2</span>: istore_0 <span class="hljs-comment">//赋值给i 10</span>
        <span class="hljs-number">3</span>: iload_0	<span class="hljs-comment">//加载到操作数栈顶</span>
        <span class="hljs-number">4</span>: istore_1 <span class="hljs-comment">//加载到局部变量表的1号位置</span>
        <span class="hljs-number">5</span>: bipush        <span class="hljs-number">20</span>
        <span class="hljs-number">7</span>: istore_0 <span class="hljs-comment">//赋值给i 20</span>
        <span class="hljs-number">8</span>: iload_1 <span class="hljs-comment">//加载局部变量表1号位置的数10到操作数栈</span>
        <span class="hljs-number">9</span>: ireturn <span class="hljs-comment">//返回操作数栈顶元素 10</span>
       <span class="hljs-number">10</span>: astore_2
       <span class="hljs-number">11</span>: bipush        <span class="hljs-number">20</span>
       <span class="hljs-number">13</span>: istore_0
       <span class="hljs-number">14</span>: aload_2 <span class="hljs-comment">//加载异常</span>
       <span class="hljs-number">15</span>: athrow <span class="hljs-comment">//抛出异常</span>
     Exception table:
        from    to  target type
            <span class="hljs-number">3</span>     <span class="hljs-number">5</span>    <span class="hljs-number">10</span>   any</code></pre>



<h4 id="Synchronized"><a href="#Synchronized" class="headerlink" title="Synchronized"></a>Synchronized</h4><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo5</span> </span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		<span class="hljs-keyword">int</span> i = <span class="hljs-number">10</span>;
		Lock lock = <span class="hljs-keyword">new</span> Lock();
		<span class="hljs-keyword">synchronized</span> (lock) &#123;
			System.out.println(i);
		&#125;
	&#125;
&#125;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Lock</span></span>&#123;&#125;</code></pre>

<p>对应字节码</p>
<pre><code class="hljs java">Code:
     stack=<span class="hljs-number">2</span>, locals=<span class="hljs-number">5</span>, args_size=<span class="hljs-number">1</span>
        <span class="hljs-number">0</span>: bipush        <span class="hljs-number">10</span>
        <span class="hljs-number">2</span>: istore_1
        3: new           #2                  // class com/nyima/JVM/day06/Lock
        <span class="hljs-number">6</span>: dup <span class="hljs-comment">//复制一份，放到操作数栈顶，用于构造函数消耗</span>
        7: invokespecial #3                  // Method com/nyima/JVM/day06/Lock."&lt;init&gt;":()V
       <span class="hljs-number">10</span>: astore_2 <span class="hljs-comment">//剩下的一份放到局部变量表的2号位置</span>
       <span class="hljs-number">11</span>: aload_2 <span class="hljs-comment">//加载到操作数栈</span>
       <span class="hljs-number">12</span>: dup <span class="hljs-comment">//复制一份，放到操作数栈，用于加锁时消耗</span>
       <span class="hljs-number">13</span>: astore_3 <span class="hljs-comment">//将操作数栈顶元素弹出，暂存到局部变量表的三号槽位。这时操作数栈中有一份对象的引用</span>
       <span class="hljs-number">14</span>: monitorenter <span class="hljs-comment">//加锁</span>
       <span class="hljs-comment">//锁住后代码块中的操作    </span>
       15: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
       <span class="hljs-number">18</span>: iload_1
       19: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
       <span class="hljs-comment">//加载局部变量表中三号槽位对象的引用，用于解锁    </span>
       <span class="hljs-number">22</span>: aload_3    
       <span class="hljs-number">23</span>: monitorexit <span class="hljs-comment">//解锁</span>
       <span class="hljs-number">24</span>: goto          <span class="hljs-number">34</span>
       <span class="hljs-comment">//异常操作    </span>
       <span class="hljs-number">27</span>: astore        <span class="hljs-number">4</span>
       <span class="hljs-number">29</span>: aload_3
       <span class="hljs-number">30</span>: monitorexit <span class="hljs-comment">//解锁</span>
       <span class="hljs-number">31</span>: aload         <span class="hljs-number">4</span>
       <span class="hljs-number">33</span>: athrow
       <span class="hljs-number">34</span>: <span class="hljs-keyword">return</span>
     <span class="hljs-comment">//可以看出，无论何时出现异常，都会跳转到27行，将异常放入局部变量中，并进行解锁操作，然后加载异常并抛出异常。      </span>
     Exception table:
        from    to  target type
           <span class="hljs-number">15</span>    <span class="hljs-number">24</span>    <span class="hljs-number">27</span>   any
           <span class="hljs-number">27</span>    <span class="hljs-number">31</span>    <span class="hljs-number">27</span>   any</code></pre>



<h3 id="3、编译期处理"><a href="#3、编译期处理" class="headerlink" title="3、编译期处理"></a>3、编译期处理</h3><p>所谓的 <strong>语法糖</strong> ，其实就是指 java 编译器把 <em>.java 源码编译为 <em>.class 字节码的过程中，</em></em>自动生成<strong>和</strong>转换**的一些代码，主要是为了减轻程序员的负担，算是 java 编译器给我们的一个额外福利</p>
<p><strong>注意</strong>，以下代码的分析，借助了 javap 工具，idea 的反编译功能，idea 插件 jclasslib 等工具。另外， 编译器转换的<strong>结果直接就是 class 字节码</strong>，只是为了便于阅读，给出了 几乎等价 的 java 源码方式，并不是编译器还会转换出中间的 java 源码，切记。 </p>
<h4 id="默认构造函数"><a href="#默认构造函数" class="headerlink" title="默认构造函数"></a>默认构造函数</h4><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Candy1</span> </span>&#123;

&#125;</code></pre>

<p>经过编译期优化后</p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Candy1</span> </span>&#123;
   <span class="hljs-comment">//这个无参构造器是java编译器帮我们加上的</span>
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Candy1</span><span class="hljs-params">()</span> </span>&#123;
      <span class="hljs-comment">//即调用父类 Object 的无参构造方法，即调用 java/lang/Object." &lt;init&gt;":()V</span>
      <span class="hljs-keyword">super</span>();
   &#125;
&#125;</code></pre>



<h4 id="自动拆装箱"><a href="#自动拆装箱" class="headerlink" title="自动拆装箱"></a>自动拆装箱</h4><p>基本类型和其包装类型的相互转换过程，称为拆装箱</p>
<p>在JDK 5以后，它们的转换可以在编译期自动完成</p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo2</span> </span>&#123;
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
      Integer x = <span class="hljs-number">1</span>;
      <span class="hljs-keyword">int</span> y = x;
   &#125;
&#125;</code></pre>

<p>转换过程如下</p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo2</span> </span>&#123;
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
      <span class="hljs-comment">//基本类型赋值给包装类型，称为装箱</span>
      Integer x = Integer.valueOf(<span class="hljs-number">1</span>);
      <span class="hljs-comment">//包装类型赋值给基本类型，称谓拆箱</span>
      <span class="hljs-keyword">int</span> y = x.intValue();
   &#125;
&#125;</code></pre>



<h4 id="泛型集合取值"><a href="#泛型集合取值" class="headerlink" title="泛型集合取值"></a>泛型集合取值</h4><p>泛型也是在 JDK 5 开始加入的特性，但 java 在<strong>编译泛型代码后</strong>会执行 <strong>泛型擦除</strong> 的动作，即泛型信息在编译为字节码之后就<strong>丢失</strong>了，实际的类型都当做了 <strong>Object</strong> 类型来处理：</p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo3</span> </span>&#123;
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
      List&lt;Integer&gt; list = <span class="hljs-keyword">new</span> ArrayList&lt;&gt;();
      list.add(<span class="hljs-number">10</span>);
      Integer x = list.get(<span class="hljs-number">0</span>);
   &#125;
&#125;</code></pre>

<p>对应字节码</p>
<pre><code class="hljs java">Code:
    stack=<span class="hljs-number">2</span>, locals=<span class="hljs-number">3</span>, args_size=<span class="hljs-number">1</span>
       0: new           #2                  // class java/util/ArrayList
       <span class="hljs-number">3</span>: dup
       4: invokespecial #3                  // Method java/util/ArrayList."&lt;init&gt;":()V
       <span class="hljs-number">7</span>: astore_1
       <span class="hljs-number">8</span>: aload_1
       <span class="hljs-number">9</span>: bipush        <span class="hljs-number">10</span>
      11: invokestatic  #4                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      <span class="hljs-comment">//这里进行了泛型擦除，实际调用的是add(Objcet o)</span>
      14: invokeinterface #5,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z

      <span class="hljs-number">19</span>: pop
      <span class="hljs-number">20</span>: aload_1
      <span class="hljs-number">21</span>: iconst_0
      <span class="hljs-comment">//这里也进行了泛型擦除，实际调用的是get(Object o)   </span>
      22: invokeinterface #6,  2            // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
<span class="hljs-comment">//这里进行了类型转换，将Object转换成了Integer</span>
      27: checkcast     #7                  // class java/lang/Integer
      <span class="hljs-number">30</span>: astore_2
      <span class="hljs-number">31</span>: <span class="hljs-keyword">return</span></code></pre>

<p>所以调用get函数取值时，有一个类型转换的操作</p>
<pre><code class="hljs java">Integer x = (Integer) list.get(<span class="hljs-number">0</span>);</code></pre>

<p>如果要将返回结果赋值给一个int类型的变量，则还有<strong>自动拆箱</strong>的操作</p>
<pre><code class="hljs java"><span class="hljs-keyword">int</span> x = (Integer) list.get(<span class="hljs-number">0</span>).intValue();</code></pre>



<h4 id="可变参数"><a href="#可变参数" class="headerlink" title="可变参数"></a>可变参数</h4><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo4</span> </span>&#123;
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">foo</span><span class="hljs-params">(String... args)</span> </span>&#123;
      <span class="hljs-comment">//将args赋值给arr，可以看出String...实际就是String[] </span>
      String[] arr = args;
      System.out.println(arr.length);
   &#125;

   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
      foo(<span class="hljs-string">"hello"</span>, <span class="hljs-string">"world"</span>);
   &#125;
&#125;</code></pre>

<p>可变参数 <strong>String…</strong> args 其实是一个 <strong>String[]</strong> args ，从代码中的赋值语句中就可以看出来。 同 样 java 编译器会在编译期间将上述代码变换为：</p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo4</span> </span>&#123;
   <span class="hljs-keyword">public</span> Demo4 &#123;&#125;

    
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">foo</span><span class="hljs-params">(String[] args)</span> </span>&#123;
      String[] arr = args;
      System.out.println(arr.length);
   &#125;

   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
      foo(<span class="hljs-keyword">new</span> String[]&#123;<span class="hljs-string">"hello"</span>, <span class="hljs-string">"world"</span>&#125;);
   &#125;
&#125;</code></pre>

<p>注意，如果调用的是foo()，即未传递参数时，等价代码为foo(new String[]{})，<strong>创建了一个空数组</strong>，而不是直接传递的null</p>
<h4 id="foreach"><a href="#foreach" class="headerlink" title="foreach"></a>foreach</h4><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo5</span> </span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
        <span class="hljs-comment">//数组赋初值的简化写法也是一种语法糖。</span>
		<span class="hljs-keyword">int</span>[] arr = &#123;<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>&#125;;
		<span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> x : arr) &#123;
			System.out.println(x);
		&#125;
	&#125;
&#125;</code></pre>

<p>编译器会帮我们转换为</p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo5</span> </span>&#123;
    <span class="hljs-keyword">public</span> Demo5 &#123;&#125;

	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		<span class="hljs-keyword">int</span>[] arr = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[]&#123;<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>&#125;;
		<span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> i=<span class="hljs-number">0</span>; i&lt;arr.length; ++i) &#123;
			<span class="hljs-keyword">int</span> x = arr[i];
			System.out.println(x);
		&#125;
	&#125;
&#125;</code></pre>



<p><strong>如果是集合使用foreach</strong></p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo5</span> </span>&#123;
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
      List&lt;Integer&gt; list = Arrays.asList(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>);
      <span class="hljs-keyword">for</span> (Integer x : list) &#123;
         System.out.println(x);
      &#125;
   &#125;
&#125;</code></pre>

<p>集合要使用foreach，需要该集合类实现了<strong>Iterable接口</strong>，因为集合的遍历需要用到<strong>迭代器Iterator</strong></p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo5</span> </span>&#123;
    <span class="hljs-keyword">public</span> Demo5 &#123;&#125;
    
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
      List&lt;Integer&gt; list = Arrays.asList(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>);
      <span class="hljs-comment">//获得该集合的迭代器</span>
      Iterator&lt;Integer&gt; iterator = list.iterator();
      <span class="hljs-keyword">while</span>(iterator.hasNext()) &#123;
         Integer x = iterator.next();
         System.out.println(x);
      &#125;
   &#125;
&#125;</code></pre>



<h4 id="switch字符串"><a href="#switch字符串" class="headerlink" title="switch字符串"></a>switch字符串</h4><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo6</span> </span>&#123;
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
      String str = <span class="hljs-string">"hello"</span>;
      <span class="hljs-keyword">switch</span> (str) &#123;
         <span class="hljs-keyword">case</span> <span class="hljs-string">"hello"</span> :
            System.out.println(<span class="hljs-string">"h"</span>);
            <span class="hljs-keyword">break</span>;
         <span class="hljs-keyword">case</span> <span class="hljs-string">"world"</span> :
            System.out.println(<span class="hljs-string">"w"</span>);
            <span class="hljs-keyword">break</span>;
         <span class="hljs-keyword">default</span>:
            <span class="hljs-keyword">break</span>;
      &#125;
   &#125;
&#125;</code></pre>

<p>在编译器中执行的操作</p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo6</span> </span>&#123;
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Demo6</span><span class="hljs-params">()</span> </span>&#123;
      
   &#125;
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
      String str = <span class="hljs-string">"hello"</span>;
      <span class="hljs-keyword">int</span> x = -<span class="hljs-number">1</span>;
      <span class="hljs-comment">//通过字符串的hashCode+value来判断是否匹配</span>
      <span class="hljs-keyword">switch</span> (str.hashCode()) &#123;
         <span class="hljs-comment">//hello的hashCode</span>
         <span class="hljs-keyword">case</span> <span class="hljs-number">99162322</span> :
            <span class="hljs-comment">//再次比较，因为字符串的hashCode有可能相等</span>
            <span class="hljs-keyword">if</span>(str.equals(<span class="hljs-string">"hello"</span>)) &#123;
               x = <span class="hljs-number">0</span>;
            &#125;
            <span class="hljs-keyword">break</span>;
         <span class="hljs-comment">//world的hashCode</span>
         <span class="hljs-keyword">case</span> <span class="hljs-number">11331880</span> :
            <span class="hljs-keyword">if</span>(str.equals(<span class="hljs-string">"world"</span>)) &#123;
               x = <span class="hljs-number">1</span>;
            &#125;
            <span class="hljs-keyword">break</span>;
         <span class="hljs-keyword">default</span>:
            <span class="hljs-keyword">break</span>;
      &#125;

      <span class="hljs-comment">//用第二个switch在进行输出判断</span>
      <span class="hljs-keyword">switch</span> (x) &#123;
         <span class="hljs-keyword">case</span> <span class="hljs-number">0</span>:
            System.out.println(<span class="hljs-string">"h"</span>);
            <span class="hljs-keyword">break</span>;
         <span class="hljs-keyword">case</span> <span class="hljs-number">1</span>:
            System.out.println(<span class="hljs-string">"w"</span>);
            <span class="hljs-keyword">break</span>;
         <span class="hljs-keyword">default</span>:
            <span class="hljs-keyword">break</span>;
      &#125;
   &#125;
&#125;</code></pre>

<p>过程说明：</p>
<ul>
<li>在编译期间，单个的switch被分为了两个<ul>
<li>第一个用来匹配字符串，并给x赋值<ul>
<li>字符串的匹配用到了字符串的hashCode，还用到了equals方法</li>
<li>使用hashCode是为了提高比较效率，使用equals是防止有hashCode冲突（如BM和C.）</li>
</ul>
</li>
<li>第二个用来根据x的值来决定输出语句</li>
</ul>
</li>
</ul>
<h4 id="switch枚举"><a href="#switch枚举" class="headerlink" title="switch枚举"></a>switch枚举</h4><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo7</span> </span>&#123;
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
      SEX sex = SEX.MALE;
      <span class="hljs-keyword">switch</span> (sex) &#123;
         <span class="hljs-keyword">case</span> MALE:
            System.out.println(<span class="hljs-string">"man"</span>);
            <span class="hljs-keyword">break</span>;
         <span class="hljs-keyword">case</span> FEMALE:
            System.out.println(<span class="hljs-string">"woman"</span>);
            <span class="hljs-keyword">break</span>;
         <span class="hljs-keyword">default</span>:
            <span class="hljs-keyword">break</span>;
      &#125;
   &#125;
&#125;

<span class="hljs-keyword">enum</span> SEX &#123;
   MALE, FEMALE;
&#125;</code></pre>

<p>编译器中执行的代码如下</p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo7</span> </span>&#123;
   <span class="hljs-comment">/**     </span>
<span class="hljs-comment">    * 定义一个合成类（仅 jvm 使用，对我们不可见）     </span>
<span class="hljs-comment">    * 用来映射枚举的 ordinal 与数组元素的关系     </span>
<span class="hljs-comment">    * 枚举的 ordinal 表示枚举对象的序号，从 0 开始     </span>
<span class="hljs-comment">    * 即 MALE 的 ordinal()=0，FEMALE 的 ordinal()=1     </span>
<span class="hljs-comment">    */</span> 
   <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> $<span class="hljs-title">MAP</span> </span>&#123;
      <span class="hljs-comment">//数组大小即为枚举元素个数，里面存放了case用于比较的数字</span>
      <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span>[] map = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">2</span>];
      <span class="hljs-keyword">static</span> &#123;
         <span class="hljs-comment">//ordinal即枚举元素对应所在的位置，MALE为0，FEMALE为1</span>
         map[SEX.MALE.ordinal()] = <span class="hljs-number">1</span>;
         map[SEX.FEMALE.ordinal()] = <span class="hljs-number">2</span>;
      &#125;
   &#125;

   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
      SEX sex = SEX.MALE;
      <span class="hljs-comment">//将对应位置枚举元素的值赋给x，用于case操作</span>
      <span class="hljs-keyword">int</span> x = $MAP.map[sex.ordinal()];
      <span class="hljs-keyword">switch</span> (x) &#123;
         <span class="hljs-keyword">case</span> <span class="hljs-number">1</span>:
            System.out.println(<span class="hljs-string">"man"</span>);
            <span class="hljs-keyword">break</span>;
         <span class="hljs-keyword">case</span> <span class="hljs-number">2</span>:
            System.out.println(<span class="hljs-string">"woman"</span>);
            <span class="hljs-keyword">break</span>;
         <span class="hljs-keyword">default</span>:
            <span class="hljs-keyword">break</span>;
      &#125;
   &#125;
&#125;

<span class="hljs-keyword">enum</span> SEX &#123;
   MALE, FEMALE;
&#125;</code></pre>



<h4 id="枚举类"><a href="#枚举类" class="headerlink" title="枚举类"></a>枚举类</h4><pre><code class="hljs java"><span class="hljs-keyword">enum</span> SEX &#123;
   MALE, FEMALE;
&#125;</code></pre>

<p>转换后的代码</p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Sex</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Enum</span>&lt;<span class="hljs-title">Sex</span>&gt; </span>&#123;   
   <span class="hljs-comment">//对应枚举类中的元素</span>
   <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Sex MALE;    
   <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Sex FEMALE;    
   <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Sex[] $VALUES;
   
    <span class="hljs-keyword">static</span> &#123;       
    	<span class="hljs-comment">//调用构造函数，传入枚举元素的值及ordinal</span>
    	MALE = <span class="hljs-keyword">new</span> Sex(<span class="hljs-string">"MALE"</span>, <span class="hljs-number">0</span>);    
        FEMALE = <span class="hljs-keyword">new</span> Sex(<span class="hljs-string">"FEMALE"</span>, <span class="hljs-number">1</span>);   
        $VALUES = <span class="hljs-keyword">new</span> Sex[]&#123;MALE, FEMALE&#125;; 
   &#125;
 	
   <span class="hljs-comment">//调用父类中的方法</span>
    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-title">Sex</span><span class="hljs-params">(String name, <span class="hljs-keyword">int</span> ordinal)</span> </span>&#123;     
        <span class="hljs-keyword">super</span>(name, ordinal);    
    &#125;
   
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Sex[] values() &#123;  
        <span class="hljs-keyword">return</span> $VALUES.clone();  
    &#125;
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Sex <span class="hljs-title">valueOf</span><span class="hljs-params">(String name)</span> </span>&#123; 
        <span class="hljs-keyword">return</span> Enum.valueOf(Sex<span class="hljs-class">.<span class="hljs-keyword">class</span>, <span class="hljs-title">name</span>)</span>;  
    &#125; 
   
&#125;</code></pre>



<h4 id="匿名内部类"><a href="#匿名内部类" class="headerlink" title="匿名内部类"></a>匿名内部类</h4><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo8</span> </span>&#123;
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
      Runnable runnable = <span class="hljs-keyword">new</span> Runnable() &#123;
         <span class="hljs-meta">@Override</span>
         <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>&#123;
            System.out.println(<span class="hljs-string">"running..."</span>);
         &#125;
      &#125;;
   &#125;
&#125;</code></pre>

<p>转换后的代码</p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo8</span> </span>&#123;
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
      <span class="hljs-comment">//用额外创建的类来创建匿名内部类对象</span>
      Runnable runnable = <span class="hljs-keyword">new</span> Demo8$<span class="hljs-number">1</span>();
   &#125;
&#125;

<span class="hljs-comment">//创建了一个额外的类，实现了Runnable接口</span>
<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo8</span>$1 <span class="hljs-keyword">implements</span> <span class="hljs-title">Runnable</span> </span>&#123;
   <span class="hljs-keyword">public</span> Demo8$<span class="hljs-number">1</span>() &#123;&#125;

   <span class="hljs-meta">@Override</span>
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>&#123;
      System.out.println(<span class="hljs-string">"running..."</span>);
   &#125;
&#125;</code></pre>



<p>如果匿名内部类中引用了<strong>局部变量</strong></p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo8</span> </span>&#123;
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
      <span class="hljs-keyword">int</span> x = <span class="hljs-number">1</span>;
      Runnable runnable = <span class="hljs-keyword">new</span> Runnable() &#123;
         <span class="hljs-meta">@Override</span>
         <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>&#123;
            System.out.println(x);
         &#125;
      &#125;;
   &#125;
&#125;</code></pre>

<p>转化后代码</p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo8</span> </span>&#123;
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
      <span class="hljs-keyword">int</span> x = <span class="hljs-number">1</span>;
      Runnable runnable = <span class="hljs-keyword">new</span> Runnable() &#123;
         <span class="hljs-meta">@Override</span>
         <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>&#123;
            System.out.println(x);
         &#125;
      &#125;;
   &#125;
&#125;

<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo8</span>$1 <span class="hljs-keyword">implements</span> <span class="hljs-title">Runnable</span> </span>&#123;
   <span class="hljs-comment">//多创建了一个变量</span>
   <span class="hljs-keyword">int</span> val$x;
   <span class="hljs-comment">//变为了有参构造器</span>
   <span class="hljs-keyword">public</span> Demo8$<span class="hljs-number">1</span>(<span class="hljs-keyword">int</span> x) &#123;
      <span class="hljs-keyword">this</span>.val$x = x;
   &#125;

   <span class="hljs-meta">@Override</span>
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>&#123;
      System.out.println(val$x);
   &#125;
&#125;</code></pre>



<h3 id="4、类加载阶段"><a href="#4、类加载阶段" class="headerlink" title="4、类加载阶段"></a>4、类加载阶段</h3><h4 id="加载"><a href="#加载" class="headerlink" title="加载"></a>加载</h4><ul>
<li>将类的字节码载入<strong>方法区</strong>（1.8后为元空间，在本地内存中）中，内部采用 C++ 的 instanceKlass 描述 java 类，它的重要 ﬁeld 有： <ul>
<li>_java_mirror 即 java 的类镜像，例如对 String 来说，它的镜像类就是 String.class，作用是把 klass 暴露给 java 使用 </li>
<li>_super 即父类</li>
<li>_ﬁelds 即成员变量 </li>
<li>_methods 即方法 </li>
<li>_constants 即常量池 </li>
<li>_class_loader 即类加载器 </li>
<li>_vtable 虚方法表 </li>
<li>_itable 接口方法</li>
</ul>
</li>
<li>如果这个类还有父类没有加载，<strong>先加载父类</strong> </li>
<li>加载和链接可能是<strong>交替运行</strong>的</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200611205050.png" srcset="/img/loading.gif" alt=""></p>
<ul>
<li>instanceKlass保存在<strong>方法区</strong>。JDK 8以后，方法区位于元空间中，而元空间又位于本地内存中</li>
<li>_java_mirror则是保存在<strong>堆内存</strong>中</li>
<li>InstanceKlass和*.class(JAVA镜像类)互相保存了对方的地址</li>
<li>类的对象在对象头中保存了*.class的地址。让对象可以通过其找到方法区中的instanceKlass，从而获取类的各种信息</li>
</ul>
<h4 id="链接"><a href="#链接" class="headerlink" title="链接"></a>链接</h4><h5 id="验证"><a href="#验证" class="headerlink" title="验证"></a>验证</h5><p>验证类是否符合 JVM规范，安全性检查</p>
<h5 id="准备"><a href="#准备" class="headerlink" title="准备"></a>准备</h5><p>为 static 变量分配空间，设置默认值</p>
<ul>
<li>static变量在JDK 7以前是存储与instanceKlass末尾。但在JDK 7以后就存储在_java_mirror末尾了</li>
<li>static变量在分配空间和赋值是在两个阶段完成的。分配空间在准备阶段完成，赋值在初始化阶段完成</li>
<li>如果 static 变量是 ﬁnal 的<strong>基本类型</strong>，以及<strong>字符串常量</strong>，那么编译阶段值就确定了，<strong>赋值在准备阶段完成</strong> </li>
<li>如果 static 变量是 ﬁnal 的，但属于<strong>引用类型</strong>，那么赋值也会在<strong>初始化阶段完成</strong></li>
</ul>
<h5 id="解析"><a href="#解析" class="headerlink" title="解析"></a>解析</h5><p><strong>HSDB的使用</strong></p>
<ul>
<li>先获得要查看的进程ID</li>
</ul>
<pre><code class="hljs ebnf"><span class="hljs-attribute">jps</span></code></pre>

<ul>
<li>打开HSDB</li>
</ul>
<pre><code class="hljs crystal">java -cp <span class="hljs-symbol">F:</span>\JAVA\JDK8.<span class="hljs-number">0</span>\<span class="hljs-class"><span class="hljs-keyword">lib</span>\<span class="hljs-title">sa</span>-<span class="hljs-title">jdi</span>.<span class="hljs-title">jar</span> <span class="hljs-title">sun</span>.<span class="hljs-title">jvm</span>.<span class="hljs-title">hotspot</span>.<span class="hljs-title">HSDB</span></span></code></pre>

<ul>
<li>运行时可能会报错，是因为<strong>缺少一个.dll的文件</strong>，我们在JDK的安装目录中找到该文件，复制到缺失的文件下即可</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200611221703.png" srcset="/img/loading.gif" alt=""></p>
<ul>
<li>定位需要的进程</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200611221857.png" srcset="/img/loading.gif" alt=""></p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200611222029.png" srcset="/img/loading.gif" alt=""></p>
<p><strong>解析的含义</strong></p>
<p>将常量池中的符号引用解析为直接引用</p>
<ul>
<li>未解析时，常量池中的看到的对象仅是符号，未真正的存在于内存中</li>
</ul>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo1</span> </span>&#123;
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> IOException, ClassNotFoundException </span>&#123;
      ClassLoader loader = Demo1<span class="hljs-class">.<span class="hljs-keyword">class</span>.<span class="hljs-title">getClassLoader</span>()</span>;
      <span class="hljs-comment">//只加载不解析</span>
      Class&lt;?&gt; c = loader.loadClass(<span class="hljs-string">"com.nyima.JVM.day8.C"</span>);
      <span class="hljs-comment">//用于阻塞主线程</span>
      System.in.read();
   &#125;
&#125;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">C</span> </span>&#123;
   D d = <span class="hljs-keyword">new</span> D();
&#125;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">D</span> </span>&#123;

&#125;</code></pre>

<ul>
<li>打开HSDB<ul>
<li>可以看到此时只加载了类C</li>
</ul>
</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200611223153.png" srcset="/img/loading.gif" alt=""></p>
<p>查看类C的常量池，可以看到类D<strong>未被解析</strong>，只是存在于常量池中的符号</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200611230658.png" srcset="/img/loading.gif" alt=""></p>
<ul>
<li><p>解析以后，会将常量池中的符号引用解析为直接引用</p>
<ul>
<li>可以看到，此时已加载并解析了类C和类D</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200611223441.png" srcset="/img/loading.gif" alt=""></p>
</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200613104723.png" srcset="/img/loading.gif" alt=""></p>
<h4 id="初始化"><a href="#初始化" class="headerlink" title="初始化"></a>初始化</h4><p>初始化阶段就是<strong>执行类构造器clinit()方法的过程</strong>，虚拟机会保证这个类的『构造方法』的线程安全</p>
<ul>
<li>clinit()方法是由编译器自动收集类中的所有类变量的<strong>赋值动作和静态语句块</strong>（static{}块）中的语句合并产生的</li>
</ul>
<p><strong>注意</strong></p>
<p>编译器收集的顺序是由语句在源文件中<strong>出现的顺序决定</strong>的，静态语句块中只能访问到定义在静态语句块之前的变量，定义在它<strong>之后</strong>的变量，在前面的静态语句块<strong>可以赋值，但是不能访问</strong>，如</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201118204542.png" srcset="/img/loading.gif" alt=""></p>
<h5 id="发生时机"><a href="#发生时机" class="headerlink" title="发生时机"></a>发生时机</h5><p><strong>类的初始化的懒惰的</strong>，以下情况会初始化</p>
<ul>
<li>main 方法所在的类，总会被首先初始化 </li>
<li>首次访问这个类的静态变量或静态方法时 </li>
<li>子类初始化，如果父类还没初始化，会引发 </li>
<li>子类访问父类的静态变量，只会触发父类的初始化</li>
<li>Class.forName </li>
<li>new 会导致初始化</li>
</ul>
<p>以下情况不会初始化</p>
<ul>
<li>访问类的 static ﬁnal 静态常量（基本类型和字符串）</li>
<li>类对象.class 不会触发初始化</li>
<li>创建该类对象的数组</li>
<li>类加载器的.loadClass方法</li>
<li>Class.forNamed的参数2为false时</li>
</ul>
<p><strong>验证类是否被初始化，可以看改类的静态代码块是否被执行</strong></p>
<h3 id="5、类加载器"><a href="#5、类加载器" class="headerlink" title="5、类加载器"></a>5、类加载器</h3><p>Java虚拟机设计团队有意把类加载阶段中的<strong>“通过一个类的全限定名来获取描述该类的二进制字节流”</strong>这个动作放到Java虚拟机外部去实现，以便让应用程序自己决定如何去获取所需的类。实现这个动作的代码被称为<strong>“类加载器”</strong>（ClassLoader）</p>
<h4 id="类与类加载器"><a href="#类与类加载器" class="headerlink" title="类与类加载器"></a>类与类加载器</h4><p>类加载器虽然只用于实现类的加载动作，但它在Java程序中起到的作用却远超类加载阶段</p>
<p>对于任意一个类，都必须由加载它的<strong>类加载器</strong>和这个<strong>类本身</strong>一起共同确立其在Java虚拟机中的唯一性，每一个类加载器，都拥有一个独立的类名称空间。这句话可以表达得更通俗一些：<strong>比较两个类是否“相等”，只有在这两个类是由同一个类加载器加载的前提下才有意义</strong>，否则，即使这两个类来源于同一个Class文件，被同一个Java虚拟机加载，只要加载它们的类加载器不同，那这两个类就必定不相等</p>
<p>以JDK 8为例</p>
<table>
<thead>
<tr>
<th>名称</th>
<th>加载的类</th>
<th>说明</th>
</tr>
</thead>
<tbody><tr>
<td>Bootstrap ClassLoader（启动类加载器）</td>
<td>JAVA_HOME/jre/lib</td>
<td>无法直接访问</td>
</tr>
<tr>
<td>Extension ClassLoader(拓展类加载器)</td>
<td>JAVA_HOME/jre/lib/ext</td>
<td>上级为Bootstrap，<strong>显示为null</strong></td>
</tr>
<tr>
<td>Application ClassLoader(应用程序类加载器)</td>
<td>classpath</td>
<td>上级为Extension</td>
</tr>
<tr>
<td>自定义类加载器</td>
<td>自定义</td>
<td>上级为Application</td>
</tr>
</tbody></table>
<h4 id="启动类加载器"><a href="#启动类加载器" class="headerlink" title="启动类加载器"></a>启动类加载器</h4><p>可通过在控制台输入指令，使得类被启动类加器加载</p>
<h4 id="拓展类加载器"><a href="#拓展类加载器" class="headerlink" title="拓展类加载器"></a>拓展类加载器</h4><p>如果classpath和JAVA_HOME/jre/lib/ext 下有同名类，加载时会使用<strong>拓展类加载器</strong>加载。当应用程序类加载器发现拓展类加载器已将该同名类加载过了，则不会再次加载</p>
<h4 id="双亲委派模式"><a href="#双亲委派模式" class="headerlink" title="双亲委派模式"></a>双亲委派模式</h4><p>双亲委派模式，即调用类加载器ClassLoader 的 loadClass 方法时，查找类的规则</p>
<p>loadClass源码</p>
<pre><code class="hljs java"><span class="hljs-keyword">protected</span> Class&lt;?&gt; loadClass(String name, <span class="hljs-keyword">boolean</span> resolve)
    <span class="hljs-keyword">throws</span> ClassNotFoundException
&#123;
    <span class="hljs-keyword">synchronized</span> (getClassLoadingLock(name)) &#123;
        <span class="hljs-comment">// 首先查找该类是否已经被该类加载器加载过了</span>
        Class&lt;?&gt; c = findLoadedClass(name);
        <span class="hljs-comment">//如果没有被加载过</span>
        <span class="hljs-keyword">if</span> (c == <span class="hljs-keyword">null</span>) &#123;
            <span class="hljs-keyword">long</span> t0 = System.nanoTime();
            <span class="hljs-keyword">try</span> &#123;
                <span class="hljs-comment">//看是否被它的上级加载器加载过了 Extension的上级是Bootstarp，但它显示为null</span>
                <span class="hljs-keyword">if</span> (parent != <span class="hljs-keyword">null</span>) &#123;
                    c = parent.loadClass(name, <span class="hljs-keyword">false</span>);
                &#125; <span class="hljs-keyword">else</span> &#123;
                    <span class="hljs-comment">//看是否被启动类加载器加载过</span>
                    c = findBootstrapClassOrNull(name);
                &#125;
            &#125; <span class="hljs-keyword">catch</span> (ClassNotFoundException e) &#123;
                <span class="hljs-comment">// ClassNotFoundException thrown if class not found</span>
                <span class="hljs-comment">// from the non-null parent class loader</span>
                <span class="hljs-comment">//捕获异常，但不做任何处理</span>
            &#125;

            <span class="hljs-keyword">if</span> (c == <span class="hljs-keyword">null</span>) &#123;
                <span class="hljs-comment">//如果还是没有找到，先让拓展类加载器调用findClass方法去找到该类，如果还是没找到，就抛出异常</span>
                <span class="hljs-comment">//然后让应用类加载器去找classpath下找该类</span>
                <span class="hljs-keyword">long</span> t1 = System.nanoTime();
                c = findClass(name);

                <span class="hljs-comment">// 记录时间</span>
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            &#125;
        &#125;
        <span class="hljs-keyword">if</span> (resolve) &#123;
            resolveClass(c);
        &#125;
        <span class="hljs-keyword">return</span> c;
    &#125;
&#125;</code></pre>



<h4 id="自定义类加载器"><a href="#自定义类加载器" class="headerlink" title="自定义类加载器"></a>自定义类加载器</h4><h5 id="使用场景"><a href="#使用场景" class="headerlink" title="使用场景"></a>使用场景</h5><ul>
<li>想加载非 classpath 随意路径中的类文件 </li>
<li>通过接口来使用实现，希望解耦时，常用在框架设计 </li>
<li>这些类希望予以隔离，不同应用的同名类都可以加载，不冲突，常见于 tomcat 容器</li>
</ul>
<h5 id="步骤"><a href="#步骤" class="headerlink" title="步骤"></a>步骤</h5><ul>
<li>继承ClassLoader父类</li>
<li>要遵从双亲委派机制，重写 ﬁndClass 方法 <ul>
<li>不是重写loadClass方法，否则不会走双亲委派机制</li>
</ul>
</li>
<li>读取类文件的字节码</li>
<li>调用父类的 deﬁneClass 方法来加载类 </li>
<li>使用者调用该类加载器的 loadClass 方法</li>
</ul>
<h4 id="破坏双亲委派模式"><a href="#破坏双亲委派模式" class="headerlink" title="破坏双亲委派模式"></a>破坏双亲委派模式</h4><ul>
<li><p>双亲委派模型的第一次“被破坏”其实发生在双亲委派模型出现之前——即JDK1.2面世以前的“远古”时代</p>
<ul>
<li>建议用户重写findClass()方法，在类加载器中的loadClass()方法中也会调用该方法</li>
</ul>
</li>
<li><p>双亲委派模型的第二次“被破坏”是由这个模型自身的缺陷导致的</p>
<ul>
<li>如果有基础类型又要调用回用户的代码，此时也会破坏双亲委派模式</li>
</ul>
</li>
<li><p>双亲委派模型的第三次“被破坏”是由于用户对程序动态性的追求而导致的</p>
<ul>
<li>这里所说的“动态性”指的是一些非常“热”门的名词：代码热替换（Hot Swap）、模块热部署（Hot Deployment）等</li>
</ul>
</li>
</ul>
<h3 id="6、运行期优化"><a href="#6、运行期优化" class="headerlink" title="6、运行期优化"></a>6、运行期优化</h3><h4 id="分层编译"><a href="#分层编译" class="headerlink" title="分层编译"></a>分层编译</h4><p>JVM 将执行状态分成了 5 个层次：</p>
<ul>
<li>0层：解释执行，用解释器将字节码翻译为机器码</li>
<li>1层：使用 C1 <strong>即时编译器</strong>编译执行（不带 proﬁling） </li>
<li>2层：使用  C1 即时编译器编译执行（带基本的profiling）</li>
<li>3层：使用 C1 即时编译器编译执行（带完全的profiling）</li>
<li>4层：使用 C2 即时编译器编译执行</li>
</ul>
<p>proﬁling 是指在运行过程中收集一些程序执行状态的数据，例如【方法的调用次数】，【循环的 回边次数】等</p>
<h5 id="即时编译器（JIT）与解释器的区别"><a href="#即时编译器（JIT）与解释器的区别" class="headerlink" title="即时编译器（JIT）与解释器的区别"></a>即时编译器（JIT）与解释器的区别</h5><ul>
<li>解释器<ul>
<li>将字节码<strong>解释</strong>为机器码，下次即使遇到相同的字节码，仍会执行重复的解释 </li>
<li>是将字节码解释为针对所有平台都通用的机器码 </li>
</ul>
</li>
<li>即时编译器 <ul>
<li>将一些字节码<strong>编译</strong>为机器码，<strong>并存入 Code Cache</strong>，下次遇到相同的代码，直接执行，无需再编译 </li>
<li>根据平台类型，生成平台特定的机器码</li>
</ul>
</li>
</ul>
<p>对于大部分的不常用的代码，我们无需耗费时间将其编译成机器码，而是采取解释执行的方式运行；另一方面，对于仅占据小部分的热点代码，我们则可以将其编译成机器码，以达到理想的运行速度。 执行效率上简单比较一下 Interpreter &lt; C1 &lt; C2，总的目标是发现热点代码（hotspot名称的由 来），并优化这些热点代码</p>
<h5 id="逃逸分析"><a href="#逃逸分析" class="headerlink" title="逃逸分析"></a>逃逸分析</h5><p>逃逸分析（Escape Analysis）简单来讲就是，Java Hotspot 虚拟机可以分析新创建对象的使用范围，并决定是否在 Java 堆上分配内存的一项技术</p>
<p>逃逸分析的 JVM 参数如下：</p>
<ul>
<li>开启逃逸分析：-XX:+DoEscapeAnalysis</li>
<li>关闭逃逸分析：-XX:-DoEscapeAnalysis</li>
<li>显示分析结果：-XX:+PrintEscapeAnalysis</li>
</ul>
<p>逃逸分析技术在 Java SE 6u23+ 开始支持，并默认设置为启用状态，可以不用额外加这个参数</p>
<p><strong>对象逃逸状态</strong></p>
<p><strong>全局逃逸（GlobalEscape）</strong></p>
<ul>
<li>即一个对象的作用范围逃出了当前方法或者当前线程，有以下几种场景：<ul>
<li>对象是一个静态变量</li>
<li>对象是一个已经发生逃逸的对象</li>
<li>对象作为当前方法的返回值</li>
</ul>
</li>
</ul>
<p><strong>参数逃逸（ArgEscape）</strong></p>
<ul>
<li>即一个对象被作为方法参数传递或者被参数引用，但在调用过程中不会发生全局逃逸，这个状态是通过被调方法的字节码确定的</li>
</ul>
<p><strong>没有逃逸</strong></p>
<ul>
<li>即方法中的对象没有发生逃逸</li>
</ul>
<p><strong>逃逸分析优化</strong></p>
<p>针对上面第三点，当一个对象<strong>没有逃逸</strong>时，可以得到以下几个虚拟机的优化</p>
<p><strong>锁消除</strong></p>
<p>我们知道线程同步锁是非常牺牲性能的，当编译器确定当前对象只有当前线程使用，那么就会移除该对象的同步锁</p>
<p>例如，StringBuffer 和 Vector 都是用 synchronized 修饰线程安全的，但大部分情况下，它们都只是在当前线程中用到，这样编译器就会优化移除掉这些锁操作</p>
<p>锁消除的 JVM 参数如下：</p>
<ul>
<li>开启锁消除：-XX:+EliminateLocks</li>
<li>关闭锁消除：-XX:-EliminateLocks</li>
</ul>
<p>锁消除在 JDK8 中都是默认开启的，并且锁消除都要建立在逃逸分析的基础上</p>
<p><strong>标量替换</strong></p>
<p>首先要明白标量和聚合量，<strong>基础类型</strong>和<strong>对象的引用</strong>可以理解为<strong>标量</strong>，它们不能被进一步分解。而能被进一步分解的量就是聚合量，比如：对象</p>
<p>对象是聚合量，它又可以被进一步分解成标量，将其成员变量分解为分散的变量，这就叫做<strong>标量替换</strong>。</p>
<p>这样，如果一个对象没有发生逃逸，那压根就不用创建它，只会在栈或者寄存器上创建它用到的成员标量，节省了内存空间，也提升了应用程序性能</p>
<p>标量替换的 JVM 参数如下：</p>
<ul>
<li>开启标量替换：-XX:+EliminateAllocations</li>
<li>关闭标量替换：-XX:-EliminateAllocations</li>
<li>显示标量替换详情：-XX:+PrintEliminateAllocations</li>
</ul>
<p>标量替换同样在 JDK8 中都是默认开启的，并且都要建立在逃逸分析的基础上</p>
<p><strong>栈上分配</strong></p>
<p>当对象没有发生逃逸时，该<strong>对象</strong>就可以通过标量替换分解成成员标量分配在<strong>栈内存</strong>中，和方法的生命周期一致，随着栈帧出栈时销毁，减少了 GC 压力，提高了应用程序性能</p>
<h4 id="方法内联"><a href="#方法内联" class="headerlink" title="方法内联"></a>方法内联</h4><h5 id="内联函数"><a href="#内联函数" class="headerlink" title="内联函数"></a><strong>内联函数</strong></h5><p>内联函数就是在程序编译时，编译器将程序中出现的内联函数的调用表达式用内联函数的函数体来直接进行替换</p>
<h5 id="JVM内联函数"><a href="#JVM内联函数" class="headerlink" title="JVM内联函数"></a><strong>JVM内联函数</strong></h5><p>C++是否为内联函数由自己决定，Java由<strong>编译器决定</strong>。Java不支持直接声明为内联函数的，如果想让他内联，你只能够向编译器提出请求: 关键字<strong>final修饰</strong> 用来指明那个函数是希望被JVM内联的，如</p>
<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">void</span> <span class="hljs-title">doSomething</span><span class="hljs-params">()</span> </span>&#123;  
        <span class="hljs-comment">// to do something  </span>
&#125;</code></pre>

<p>总的来说，一般的函数都不会被当做内联函数，只有声明了final后，编译器才会考虑是不是要把你的函数变成内联函数</p>
<p>JVM内建有许多运行时优化。首先<strong>短方法</strong>更利于JVM推断。流程更明显，作用域更短，副作用也更明显。如果是长方法JVM可能直接就跪了。</p>
<p>第二个原因则更重要：<strong>方法内联</strong> </p>
<p> 如果JVM监测到一些<strong>小方法被频繁的执行</strong>，它会把方法的调用替换成方法体本身，如：</p>
<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> <span class="hljs-title">add4</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x1, <span class="hljs-keyword">int</span> x2, <span class="hljs-keyword">int</span> x3, <span class="hljs-keyword">int</span> x4)</span> </span>&#123; 
		<span class="hljs-comment">//这里调用了add2方法</span>
        <span class="hljs-keyword">return</span> add2(x1, x2) + add2(x3, x4);  
    &#125;  

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> <span class="hljs-title">add2</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x1, <span class="hljs-keyword">int</span> x2)</span> </span>&#123;  
        <span class="hljs-keyword">return</span> x1 + x2;  
    &#125;</code></pre>

<p>方法调用被替换后</p>
<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> <span class="hljs-title">add4</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x1, <span class="hljs-keyword">int</span> x2, <span class="hljs-keyword">int</span> x3, <span class="hljs-keyword">int</span> x4)</span> </span>&#123;  
    	<span class="hljs-comment">//被替换为了方法本身</span>
        <span class="hljs-keyword">return</span> x1 + x2 + x3 + x4;  
    &#125;</code></pre>



<h4 id="反射优化"><a href="#反射优化" class="headerlink" title="反射优化"></a>反射优化</h4><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Reflect1</span> </span>&#123;
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">foo</span><span class="hljs-params">()</span> </span>&#123;
      System.out.println(<span class="hljs-string">"foo..."</span>);
   &#125;

   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> NoSuchMethodException, InvocationTargetException, IllegalAccessException </span>&#123;
      Method foo = Demo3.class.getMethod("foo");
      <span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i&lt;=<span class="hljs-number">16</span>; i++) &#123;
         foo.invoke(<span class="hljs-keyword">null</span>);
      &#125;
   &#125;
&#125;</code></pre>

<p>foo.invoke 前面 0 ~ 15 次调用使用的是 MethodAccessor 的 NativeMethodAccessorImpl 实现</p>
<p>invoke方法源码</p>
<pre><code class="hljs java"><span class="hljs-meta">@CallerSensitive</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> Object <span class="hljs-title">invoke</span><span class="hljs-params">(Object obj, Object... args)</span></span>
<span class="hljs-function">    <span class="hljs-keyword">throws</span> IllegalAccessException, IllegalArgumentException,</span>
<span class="hljs-function">       InvocationTargetException</span>
<span class="hljs-function"></span>&#123;
    <span class="hljs-keyword">if</span> (!override) &#123;
        <span class="hljs-keyword">if</span> (!Reflection.quickCheckMemberAccess(clazz, modifiers)) &#123;
            Class&lt;?&gt; caller = Reflection.getCallerClass();
            checkAccess(caller, clazz, obj, modifiers);
        &#125;
    &#125;
    <span class="hljs-comment">//MethodAccessor是一个接口，有3个实现类，其中有一个是抽象类</span>
    MethodAccessor ma = methodAccessor;             <span class="hljs-comment">// read volatile</span>
    <span class="hljs-keyword">if</span> (ma == <span class="hljs-keyword">null</span>) &#123;
        ma = acquireMethodAccessor();
    &#125;
    <span class="hljs-keyword">return</span> ma.invoke(obj, args);
&#125;</code></pre>

<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200614133554.png" srcset="/img/loading.gif" alt=""></p>
<p>会由DelegatingMehodAccessorImpl去调用NativeMethodAccessorImpl</p>
<p>NativeMethodAccessorImpl源码</p>
<pre><code class="hljs reasonml"><span class="hljs-keyword">class</span> NativeMethodAccessorImpl extends MethodAccessorImpl &#123;
    <span class="hljs-keyword">private</span> final Method <span class="hljs-keyword">method</span>;
    <span class="hljs-keyword">private</span> DelegatingMethodAccessorImpl parent;
    <span class="hljs-keyword">private</span> <span class="hljs-built_in">int</span> numInvocations;

    <span class="hljs-constructor">NativeMethodAccessorImpl(Method <span class="hljs-params">var1</span>)</span> &#123;
        this.<span class="hljs-keyword">method</span> = var1;
    &#125;
	
	<span class="hljs-comment">//每次进行反射调用，会让numInvocation与ReflectionFactory.inflationThreshold的值（15）进行比较，并使使得numInvocation的值加一</span>
	<span class="hljs-comment">//如果numInvocation&gt;ReflectionFactory.inflationThreshold，则会调用本地方法invoke0方法</span>
    public Object invoke(Object var1, Object<span class="hljs-literal">[]</span> var2) throws IllegalArgumentException, InvocationTargetException &#123;
        <span class="hljs-keyword">if</span> (++this.numInvocations &gt; <span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">ReflectionFactory</span>.</span></span>inflation<span class="hljs-constructor">Threshold()</span><span class="hljs-operator"> &amp;&amp; </span>!<span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">ReflectUtil</span>.</span></span>is<span class="hljs-constructor">VMAnonymousClass(<span class="hljs-params">this</span>.<span class="hljs-params">method</span>.<span class="hljs-params">getDeclaringClass</span>()</span>)) &#123;
            MethodAccessorImpl var3 = (MethodAccessorImpl)(<span class="hljs-keyword">new</span> <span class="hljs-constructor">MethodAccessorGenerator()</span>).generate<span class="hljs-constructor">Method(<span class="hljs-params">this</span>.<span class="hljs-params">method</span>.<span class="hljs-params">getDeclaringClass</span>()</span>, this.<span class="hljs-keyword">method</span>.get<span class="hljs-constructor">Name()</span>, this.<span class="hljs-keyword">method</span>.get<span class="hljs-constructor">ParameterTypes()</span>, this.<span class="hljs-keyword">method</span>.get<span class="hljs-constructor">ReturnType()</span>, this.<span class="hljs-keyword">method</span>.get<span class="hljs-constructor">ExceptionTypes()</span>, this.<span class="hljs-keyword">method</span>.get<span class="hljs-constructor">Modifiers()</span>);
            this.parent.set<span class="hljs-constructor">Delegate(<span class="hljs-params">var3</span>)</span>;
        &#125;

        return invoke0(this.<span class="hljs-keyword">method</span>, var1, var2);
    &#125;

    void set<span class="hljs-constructor">Parent(DelegatingMethodAccessorImpl <span class="hljs-params">var1</span>)</span> &#123;
        this.parent = var1;
    &#125;

    <span class="hljs-keyword">private</span> static native Object invoke0(Method var0, Object var1, Object<span class="hljs-literal">[]</span> var2);
&#125;</code></pre>

<pre><code class="hljs java"><span class="hljs-comment">//ReflectionFactory.inflationThreshold()方法的返回值</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> inflationThreshold = <span class="hljs-number">15</span>;</code></pre>

<ul>
<li>一开始if条件不满足，就会调用本地方法invoke0</li>
<li>随着numInvocation的增大，当它大于ReflectionFactory.inflationThreshold的值16时，就会本地方法访问器替换为一个运行时动态生成的访问器，来提高效率<ul>
<li>这时会从反射调用变为<strong>正常调用</strong>，即直接调用 Reflect1.foo()</li>
</ul>
</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200614135011.png" srcset="/img/loading.gif" alt=""></p>
<h2 id="五、内存模型"><a href="#五、内存模型" class="headerlink" title="五、内存模型"></a>五、内存模型</h2><p>内存模型内容详见 <a href="https://nyimac.gitee.io/2020/06/08/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/#%E5%9B%9B%E3%80%81%E5%85%B1%E4%BA%AB%E6%A8%A1%E5%9E%8B%E4%B9%8B%E5%86%85%E5%AD%98">JAVA并发 第四章</a></p>

            </article>
            <hr>
            <div>
              <div class="post-metas mb-3">
                
                  <div class="post-meta mr-3">
                    <i class="iconfont icon-category"></i>
                    
                      <a class="hover-with-bg" href="/categories/Java/">Java</a>
                    
                  </div>
                
                
              </div>
              
                <p class="note note-warning">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-sa/4.0/deed.zh" target="_blank" rel="nofollow noopener noopener">CC BY-SA 4.0 协议</a> ，转载请注明出处！</p>
              
              
                <div class="post-prevnext row">
                  <div class="post-prev col-6">
                    
                    
                      <a href="/2020/08/11/MySQL%E5%9F%BA%E7%A1%80/">
                        <i class="iconfont icon-arrowleft"></i>
                        <span class="hidden-mobile">MySQL基础</span>
                        <span class="visible-mobile">Previous</span>
                      </a>
                    
                  </div>
                  <div class="post-next col-6">
                    
                    
                      <a href="/2020/06/17/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/">
                        <span class="hidden-mobile">Java数据结构与算法</span>
                        <span class="visible-mobile">Next</span>
                        <i class="iconfont icon-arrowright"></i>
                      </a>
                    
                  </div>
                </div>
              
            </div>

            
              <!-- Comments -->
              <div class="comments" id="comments">
                
                
  <div class="disqus" style="width:100%">
    <div id="disqus_thread"></div>
    <script type="text/javascript">
      function loadDisqus() {
        var disqus_config = function () {
          this.page.url = 'http://nyimac.gitee.io/2020/07/03/JVM学习/';
          this.page.identifier = '/2020/07/03/JVM学习/';
        };
        (function () {
          var d = document,
            s = d.createElement('script');
          s.src = '//' + '' + '.disqus.com/embed.js';
          s.setAttribute('data-timestamp', new Date());
          (d.head || d.body).appendChild(s);
        })();
      }
      createObserver(loadDisqus, 'disqus_thread');
    </script>
    <noscript>Please enable JavaScript to view the
      <a href="https://disqus.com/?ref_noscript" target="_blank" rel="nofollow noopener noopener">comments powered by Disqus.</a>
    </noscript>
  </div>


              </div>
            
          </div>
        </div>
      </div>
    </div>
    
      <div class="d-none d-lg-block col-lg-2 toc-container" id="toc-ctn">
        <div id="toc">
  <p class="toc-header"><i class="iconfont icon-list"></i>&nbsp;TOC</p>
  <div id="tocbot"></div>
</div>

      </div>
    
  </div>
</div>

<!-- Custom -->


    
  </main>

  
    <a id="scroll-top-button" href="#" role="button">
      <i class="iconfont icon-arrowup" aria-hidden="true"></i>
    </a>
  

  
    <div class="modal fade" id="modalSearch" tabindex="-1" role="dialog" aria-labelledby="ModalLabel"
     aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header text-center">
        <h4 class="modal-title w-100 font-weight-bold">Search</h4>
        <button type="button" id="local-search-close" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body mx-3">
        <div class="md-form mb-5">
          <input type="text" id="local-search-input" class="form-control validate">
          <label data-error="x" data-success="v"
                 for="local-search-input">keyword</label>
        </div>
        <div class="list-group" id="local-search-result"></div>
      </div>
    </div>
  </div>
</div>
  

  

  

  <footer class="mt-5">
  <div class="text-center py-3">
    <div>
      <a href="https://hexo.io" target="_blank" rel="nofollow noopener"><span>Hexo</span></a>
      <i class="iconfont icon-love"></i>
      <a href="https://github.com/fluid-dev/hexo-theme-fluid" target="_blank" rel="nofollow noopener">
        <span>Fluid</span></a>
    </div>
    
  <div class="statistics">
    
    

    
      
        <!-- 不蒜子统计PV -->
        <span id="busuanzi_container_site_pv" style="display: none">
            总访问量 
            <span id="busuanzi_value_site_pv"></span>
             次
          </span>
      
      
        <!-- 不蒜子统计UV -->
        <span id="busuanzi_container_site_uv" style="display: none">
            总访客数 
            <span id="busuanzi_value_site_uv"></span>
             人
          </span>
      
    
  </div>


    

    
  </div>
</footer>

<!-- SCRIPTS -->
<script  src="https://cdn.staticfile.org/jquery/3.4.1/jquery.min.js" ></script>
<script  src="https://cdn.staticfile.org/twitter-bootstrap/4.4.1/js/bootstrap.min.js" ></script>
<script  src="/js/debouncer.js" ></script>
<script  src="/js/main.js" ></script>

<!-- Plugins -->


  
    <script  src="/js/lazyload.js" ></script>
  



  <script defer src="https://cdn.staticfile.org/clipboard.js/2.0.6/clipboard.min.js" ></script>
  <script  src="/js/clipboard-use.js" ></script>



  <script defer src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js" ></script>





  <script  src="https://cdn.staticfile.org/tocbot/4.11.1/tocbot.min.js" ></script>
  <script>
    $(document).ready(function () {
      var boardCtn = $('#board-ctn');
      var boardTop = boardCtn.offset().top;

      tocbot.init({
        tocSelector: '#tocbot',
        contentSelector: 'article.markdown-body',
        headingSelector: 'h1,h2,h3,h4,h5,h6',
        linkClass: 'tocbot-link',
        activeLinkClass: 'tocbot-active-link',
        listClass: 'tocbot-list',
        isCollapsedClass: 'tocbot-is-collapsed',
        collapsibleClass: 'tocbot-is-collapsible',
        collapseDepth: 0,
        scrollSmooth: true,
        headingsOffset: -boardTop
      });
      if ($('.toc-list-item').length > 0) {
        $('#toc').css('visibility', 'visible');
      }
    });
  </script>



  <script  src="https://cdn.staticfile.org/typed.js/2.0.11/typed.min.js" ></script>
  <script>
    var typed = new Typed('#subtitle', {
      strings: [
        '  ',
        "JVM&nbsp;",
      ],
      cursorChar: "_",
      typeSpeed: 70,
      loop: false,
    });
    typed.stop();
    $(document).ready(function () {
      $(".typed-cursor").addClass("h2");
      typed.start();
    });
  </script>



  <script  src="https://cdn.staticfile.org/anchor-js/4.2.2/anchor.min.js" ></script>
  <script>
    anchors.options = {
      placement: "right",
      visible: "hover",
      
    };
    var el = "h1,h2,h3,h4,h5,h6".split(",");
    var res = [];
    for (item of el) {
      res.push(".markdown-body > " + item)
    }
    anchors.add(res.join(", "))
  </script>



  <script  src="/js/local-search.js" ></script>
  <script>
    var path = "/local-search.xml";
    var inputArea = document.querySelector("#local-search-input");
    inputArea.onclick = function () {
      searchFunc(path, 'local-search-input', 'local-search-result');
      this.onclick = null
    }
  </script>



  <script  src="https://cdn.staticfile.org/fancybox/3.5.7/jquery.fancybox.min.js" ></script>
  <link  rel="stylesheet" href="https://cdn.staticfile.org/fancybox/3.5.7/jquery.fancybox.min.css" />

  <script>
    $('#post img:not(.no-zoom img, img[no-zoom]), img[zoom]').each(
      function () {
        var element = document.createElement('a');
        $(element).attr('data-fancybox', 'images');
        $(element).attr('href', $(this).attr('src'));
        $(this).wrap(element);
      }
    );
  </script>

















  
    <!-- Baidu Analytics -->
    <script defer>
      var _hmt = _hmt || [];
      (function () {
        var hm = document.createElement("script");
        hm.src = "https://hm.baidu.com/hm.js?ba41ec605b9b7320e120275462e4035b";
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(hm, s);
      })();
    </script>
  

  

  

  

  

  





</body>
</html>
